mirror of
https://github.com/lukaszraczylo/claude-mnemonic.git
synced 2026-11-01 07:34:29 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0845aeb58a | ||
|
|
92d92ecadb | ||
|
|
ab0c9a01ef | ||
|
|
f19c247278 | ||
|
|
8cddc88ab6 | ||
|
|
c9c0d988bc |
@@ -34,5 +34,5 @@ require (
|
||||
github.com/sugarme/regexpset v0.0.0-20200920021344-4d4ec8eaf93c // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.5 // indirect
|
||||
golang.org/x/sys v0.49.0 // indirect
|
||||
golang.org/x/text v0.42.0 // indirect
|
||||
golang.org/x/text v0.43.0 // indirect
|
||||
)
|
||||
|
||||
@@ -51,8 +51,8 @@ golang.org/x/sync v0.24.0 h1:iqiDrGoDgyuvAKrhLQthUEzU/R378N78gXbBjxed7oA=
|
||||
golang.org/x/sync v0.24.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
|
||||
golang.org/x/sys v0.49.0 h1:XbzkgYJHdqh/8m2Uu0W/dQv8nktxx4BFHp1M0gROTXA=
|
||||
golang.org/x/sys v0.49.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
|
||||
golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI=
|
||||
golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E=
|
||||
golang.org/x/text v0.43.0 h1:1QivrlhwAsnMOcqgOdl9my7s9OyZl8O5/UGUTJVXoWc=
|
||||
golang.org/x/text v0.43.0/go.mod h1:hbSIYA/amXcRXTFSZqh8tWHEoqRvvbKPJ2uz/KU+a+s=
|
||||
gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
|
||||
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
|
||||
gorm.io/gorm v1.31.2 h1:3o8FXNo9v9S858gil+3LlZA1LkCOzgb4g5BL64FgaCo=
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
//go:build fts5
|
||||
|
||||
package gorm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/lukaszraczylo/claude-mnemonic/pkg/models"
|
||||
)
|
||||
|
||||
// An archived note is kept, but it must stay out of search, context, lists, counts and the vector rebuild, and an
|
||||
// export (which asks for it) still has it.
|
||||
func TestArchivedObservationsAreKeptButHiddenEverywhereExceptAnExport(t *testing.T) {
|
||||
s, store, cleanup := testObservationStore(t)
|
||||
defer cleanup()
|
||||
ctx := context.Background()
|
||||
|
||||
var ids []int64
|
||||
for _, title := range []string{"Zebra ledger reconciliation", "Quokka deployment checklist", "Ocelot retry budget"} {
|
||||
id, _, err := s.StoreObservation(ctx, "claude-1", "proj",
|
||||
&models.ParsedObservation{Type: models.ObsTypeDiscovery, Title: title, Narrative: title + " notes"}, 1, 10)
|
||||
require.NoError(t, err)
|
||||
ids = append(ids, id)
|
||||
time.Sleep(2 * time.Millisecond)
|
||||
}
|
||||
archivedID := ids[0] // the zebra note
|
||||
require.NoError(t, s.ArchiveObservation(ctx, archivedID, "test"))
|
||||
|
||||
has := func(obs []*models.Observation) bool {
|
||||
for _, o := range obs {
|
||||
if o.ID == archivedID {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
recent, err := s.GetRecentObservations(ctx, "proj", 10)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, has(recent), "context injection does not see it")
|
||||
|
||||
strict, err := s.GetObservationsByProjectStrict(ctx, "proj", 10)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, has(strict))
|
||||
|
||||
all, err := s.GetAllRecentObservations(ctx, 10)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, has(all))
|
||||
|
||||
byID, err := s.GetObservationsByIDs(ctx, ids, "default", 0)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, has(byID), "a vector hit for it is dropped when it is loaded")
|
||||
assert.Len(t, byID, 2)
|
||||
|
||||
ordered, err := s.GetObservationsByIDsPreserveOrder(ctx, ids)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, has(ordered))
|
||||
|
||||
fts, err := s.SearchObservationsFTS(ctx, "zebra ledger", "proj", 10)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, has(fts), "full-text search does not find it")
|
||||
other, err := s.SearchObservationsFTS(ctx, "quokka deployment", "proj", 10)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, other, 1, "and still finds the others")
|
||||
|
||||
n, err := s.GetObservationCount(ctx, "proj")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 2, n, "the count is of notes that can be used")
|
||||
|
||||
rebuild, err := s.GetAllObservations(ctx)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, has(rebuild), "a vector rebuild does not put it back into the index")
|
||||
|
||||
listed, total, err := s.GetAllRecentObservationsPaginated(ctx, 10, 0, false)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, has(listed))
|
||||
assert.EqualValues(t, 2, total)
|
||||
projListed, projTotal, err := s.GetObservationsByProjectStrictPaginated(ctx, "proj", 10, 0, false)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, has(projListed))
|
||||
assert.EqualValues(t, 2, projTotal)
|
||||
|
||||
exported, total, err := s.GetAllRecentObservationsPaginated(ctx, 10, 0, true)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, has(exported), "an export asks for them")
|
||||
assert.EqualValues(t, 3, total)
|
||||
projExported, _, err := s.GetObservationsByProjectStrictPaginated(ctx, "proj", 10, 0, true)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, has(projExported))
|
||||
|
||||
// Kept: the row is still there, and restoring it makes it visible again.
|
||||
one, err := s.GetObservationByID(ctx, archivedID)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, one)
|
||||
require.NoError(t, s.UnarchiveObservation(ctx, archivedID))
|
||||
back, err := s.SearchObservationsFTS(ctx, "zebra ledger", "proj", 10)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, has(back))
|
||||
var rows int64
|
||||
require.NoError(t, store.DB.Model(&Observation{}).Where("project = ?", "proj").Count(&rows).Error)
|
||||
assert.EqualValues(t, 3, rows)
|
||||
}
|
||||
@@ -290,7 +290,7 @@ func (s *ObservationStore) GetObservationsByIDs(ctx context.Context, ids []int64
|
||||
}
|
||||
|
||||
var dbObservations []Observation
|
||||
query := s.db.WithContext(ctx).Where("id IN ?", ids)
|
||||
query := s.db.WithContext(ctx).Scopes(notArchivedFilter()).Where("id IN ?", ids)
|
||||
|
||||
// Apply ordering
|
||||
switch orderBy {
|
||||
@@ -329,7 +329,7 @@ func (s *ObservationStore) GetObservationsByIDsPreserveOrder(ctx context.Context
|
||||
|
||||
// Fetch all observations in a single query
|
||||
var dbObservations []Observation
|
||||
err := s.db.WithContext(ctx).Where("id IN ?", ids).Find(&dbObservations).Error
|
||||
err := s.db.WithContext(ctx).Scopes(notArchivedFilter()).Where("id IN ?", ids).Find(&dbObservations).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -380,7 +380,7 @@ func (s *ObservationStore) BatchGetObservationsWithScores(ctx context.Context, i
|
||||
func (s *ObservationStore) GetRecentObservations(ctx context.Context, project string, limit int) ([]*models.Observation, error) {
|
||||
var dbObservations []Observation
|
||||
err := s.db.WithContext(ctx).
|
||||
Scopes(projectScopeFilter(project), importanceOrdering()).
|
||||
Scopes(projectScopeFilter(project), notArchivedFilter(), importanceOrdering()).
|
||||
Limit(limit).
|
||||
Find(&dbObservations).Error
|
||||
|
||||
@@ -430,7 +430,7 @@ func (s *ObservationStore) GetObservationsByProjectStrict(ctx context.Context, p
|
||||
var dbObservations []Observation
|
||||
err := s.db.WithContext(ctx).
|
||||
Where("project = ?", project).
|
||||
Scopes(importanceOrdering()).
|
||||
Scopes(notArchivedFilter(), importanceOrdering()).
|
||||
Limit(limit).
|
||||
Find(&dbObservations).Error
|
||||
|
||||
@@ -447,6 +447,7 @@ func (s *ObservationStore) GetObservationCount(ctx context.Context, project stri
|
||||
err := s.db.WithContext(ctx).
|
||||
Model(&Observation{}).
|
||||
Where("project = ?", project).
|
||||
Scopes(notArchivedFilter()).
|
||||
Count(&count).Error
|
||||
|
||||
return int(count), err
|
||||
@@ -456,7 +457,7 @@ func (s *ObservationStore) GetObservationCount(ctx context.Context, project stri
|
||||
func (s *ObservationStore) GetAllRecentObservations(ctx context.Context, limit int) ([]*models.Observation, error) {
|
||||
var dbObservations []Observation
|
||||
err := s.db.WithContext(ctx).
|
||||
Scopes(importanceOrdering()).
|
||||
Scopes(notArchivedFilter(), importanceOrdering()).
|
||||
Limit(limit).
|
||||
Find(&dbObservations).Error
|
||||
|
||||
@@ -467,19 +468,21 @@ func (s *ObservationStore) GetAllRecentObservations(ctx context.Context, limit i
|
||||
return toModelObservations(dbObservations), nil
|
||||
}
|
||||
|
||||
// GetAllRecentObservationsPaginated retrieves recent observations with pagination.
|
||||
func (s *ObservationStore) GetAllRecentObservationsPaginated(ctx context.Context, limit, offset int) ([]*models.Observation, int64, error) {
|
||||
// GetAllRecentObservationsPaginated retrieves recent observations with pagination. Archived notes are left out unless
|
||||
// includeArchived is set (an export asks for them).
|
||||
func (s *ObservationStore) GetAllRecentObservationsPaginated(ctx context.Context, limit, offset int, includeArchived bool) ([]*models.Observation, int64, error) {
|
||||
var dbObservations []Observation
|
||||
var total int64
|
||||
visible := visibleScope(includeArchived)
|
||||
|
||||
// Get total count
|
||||
if err := s.db.WithContext(ctx).Model(&Observation{}).Count(&total).Error; err != nil {
|
||||
if err := s.db.WithContext(ctx).Model(&Observation{}).Scopes(visible).Count(&total).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// Get paginated results
|
||||
err := s.db.WithContext(ctx).
|
||||
Scopes(importanceOrdering()).
|
||||
Scopes(visible, importanceOrdering()).
|
||||
Limit(limit).
|
||||
Offset(offset).
|
||||
Find(&dbObservations).Error
|
||||
@@ -491,20 +494,22 @@ func (s *ObservationStore) GetAllRecentObservationsPaginated(ctx context.Context
|
||||
return toModelObservations(dbObservations), total, nil
|
||||
}
|
||||
|
||||
// GetObservationsByProjectStrictPaginated retrieves observations strictly from a project with pagination.
|
||||
func (s *ObservationStore) GetObservationsByProjectStrictPaginated(ctx context.Context, project string, limit, offset int) ([]*models.Observation, int64, error) {
|
||||
// GetObservationsByProjectStrictPaginated retrieves observations strictly from a project with pagination. Archived notes
|
||||
// are left out unless includeArchived is set (an export asks for them).
|
||||
func (s *ObservationStore) GetObservationsByProjectStrictPaginated(ctx context.Context, project string, limit, offset int, includeArchived bool) ([]*models.Observation, int64, error) {
|
||||
var dbObservations []Observation
|
||||
var total int64
|
||||
visible := visibleScope(includeArchived)
|
||||
|
||||
// Get total count for project
|
||||
if err := s.db.WithContext(ctx).Model(&Observation{}).Where("project = ?", project).Count(&total).Error; err != nil {
|
||||
if err := s.db.WithContext(ctx).Model(&Observation{}).Where("project = ?", project).Scopes(visible).Count(&total).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// Get paginated results
|
||||
err := s.db.WithContext(ctx).
|
||||
Where("project = ?", project).
|
||||
Scopes(importanceOrdering()).
|
||||
Scopes(visible, importanceOrdering()).
|
||||
Limit(limit).
|
||||
Offset(offset).
|
||||
Find(&dbObservations).Error
|
||||
@@ -516,11 +521,13 @@ func (s *ObservationStore) GetObservationsByProjectStrictPaginated(ctx context.C
|
||||
return toModelObservations(dbObservations), total, nil
|
||||
}
|
||||
|
||||
// GetAllObservations retrieves all observations (for vector rebuild).
|
||||
// GetAllObservations retrieves all observations that are not archived (for vector rebuild: an archived note stays out of
|
||||
// the vector index).
|
||||
// Note: For large datasets, prefer GetAllObservationsIterator to avoid memory issues.
|
||||
func (s *ObservationStore) GetAllObservations(ctx context.Context) ([]*models.Observation, error) {
|
||||
var dbObservations []Observation
|
||||
err := s.db.WithContext(ctx).
|
||||
Scopes(notArchivedFilter()).
|
||||
Order("id").
|
||||
Find(&dbObservations).Error
|
||||
|
||||
@@ -606,6 +613,7 @@ func (s *ObservationStore) SearchObservationsFTS(ctx context.Context, query, pro
|
||||
JOIN observations_fts fts ON o.id = fts.rowid
|
||||
WHERE observations_fts MATCH ?
|
||||
AND (o.project = ? OR o.scope = 'global')
|
||||
AND COALESCE(o.is_archived, 0) = 0
|
||||
ORDER BY rank, COALESCE(o.importance_score, 1.0) DESC
|
||||
LIMIT ?
|
||||
`
|
||||
@@ -660,7 +668,7 @@ func (s *ObservationStore) searchObservationsLike(ctx context.Context, keywords
|
||||
|
||||
// Build WHERE clause
|
||||
whereClause := strings.Join(conditions, " OR ")
|
||||
fullWhere := "(" + whereClause + ") AND (project = ? OR scope = 'global')"
|
||||
fullWhere := "(" + whereClause + ") AND (project = ? OR scope = 'global') AND COALESCE(is_archived, 0) = 0"
|
||||
args = append(args, project)
|
||||
|
||||
var dbObservations []Observation
|
||||
@@ -958,6 +966,21 @@ func projectScopeFilter(project string) func(*gorm.DB) *gorm.DB {
|
||||
}
|
||||
}
|
||||
|
||||
// notArchivedFilter hides archived notes: an archived note is kept, but it must stay out of search, context and lists.
|
||||
func notArchivedFilter() func(*gorm.DB) *gorm.DB {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("COALESCE(is_archived, 0) = 0")
|
||||
}
|
||||
}
|
||||
|
||||
// visibleScope hides archived notes unless they are asked for (an export keeps them; lists and counts do not).
|
||||
func visibleScope(includeArchived bool) func(*gorm.DB) *gorm.DB {
|
||||
if includeArchived {
|
||||
return func(db *gorm.DB) *gorm.DB { return db }
|
||||
}
|
||||
return notArchivedFilter()
|
||||
}
|
||||
|
||||
// activeObservationFilter filters for active (non-archived, non-superseded) observations.
|
||||
// This is more efficient than chaining notSupersededFilter + notArchivedFilter
|
||||
// as it produces a single WHERE clause for the query optimizer.
|
||||
|
||||
@@ -28,6 +28,8 @@ func (s *Service) handleGetObservations(w http.ResponseWriter, r *http.Request)
|
||||
pagination := gorm.ParsePaginationParams(r, DefaultObservationsLimit)
|
||||
project := r.URL.Query().Get("project")
|
||||
query := r.URL.Query().Get("query")
|
||||
// Archived notes are not listed unless asked for.
|
||||
includeArchived := r.URL.Query().Get("include_archived") == "true"
|
||||
|
||||
// Validate project name to prevent path traversal
|
||||
if err := ValidateProjectName(project); err != nil {
|
||||
@@ -60,10 +62,10 @@ func (s *Service) handleGetObservations(w http.ResponseWriter, r *http.Request)
|
||||
if !usedVector {
|
||||
if project != "" {
|
||||
// Strict project filtering for dashboard - only observations from this project
|
||||
observations, total, err = s.observationStore.GetObservationsByProjectStrictPaginated(ctx, project, pagination.Limit, pagination.Offset)
|
||||
observations, total, err = s.observationStore.GetObservationsByProjectStrictPaginated(ctx, project, pagination.Limit, pagination.Offset, includeArchived)
|
||||
} else {
|
||||
// All projects
|
||||
observations, total, err = s.observationStore.GetAllRecentObservationsPaginated(ctx, pagination.Limit, pagination.Offset)
|
||||
observations, total, err = s.observationStore.GetAllRecentObservationsPaginated(ctx, pagination.Limit, pagination.Offset, includeArchived)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -300,6 +300,8 @@ func (s *Service) handleArchiveObservations(w http.ResponseWriter, r *http.Reque
|
||||
Int("failed", len(failedIDs)).
|
||||
Msg("Observations archived")
|
||||
|
||||
s.dropArchivedFromVectors(archivedIDs)
|
||||
|
||||
// Invalidate cache if any observations were archived
|
||||
if len(archivedIDs) > 0 {
|
||||
if req.Project != "" {
|
||||
@@ -322,6 +324,22 @@ func (s *Service) handleArchiveObservations(w http.ResponseWriter, r *http.Reque
|
||||
writeJSON(w, response)
|
||||
}
|
||||
|
||||
// dropArchivedFromVectors takes archived notes out of the vector index, so search cannot return them (unarchiving puts
|
||||
// them back). Every path that archives notes calls it.
|
||||
func (s *Service) dropArchivedFromVectors(ids []int64) {
|
||||
if len(ids) == 0 || s.vectorSync == nil {
|
||||
return
|
||||
}
|
||||
ids = append([]int64(nil), ids...)
|
||||
s.asyncVectorSync(func() {
|
||||
ctx, cancel := context.WithTimeout(s.ctx, 10*time.Second)
|
||||
defer cancel()
|
||||
if err := s.vectorSync.DeleteObservations(ctx, ids); err != nil && s.ctx.Err() == nil {
|
||||
log.Warn().Err(err).Int("count", len(ids)).Msg("Failed to drop archived observations from sqlite-vec")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// handleUnarchiveObservation restores an archived observation.
|
||||
func (s *Service) handleUnarchiveObservation(w http.ResponseWriter, r *http.Request) {
|
||||
idStr := chi.URLParam(r, "id")
|
||||
@@ -336,6 +354,21 @@ func (s *Service) handleUnarchiveObservation(w http.ResponseWriter, r *http.Requ
|
||||
return
|
||||
}
|
||||
|
||||
// Archiving dropped the note's vectors; put them back so search finds it again.
|
||||
if s.vectorSync != nil {
|
||||
s.asyncVectorSync(func() {
|
||||
ctx, cancel := context.WithTimeout(s.ctx, 10*time.Second)
|
||||
defer cancel()
|
||||
obs, err := s.observationStore.GetObservationByID(ctx, id)
|
||||
if err != nil || obs == nil {
|
||||
return
|
||||
}
|
||||
if err := s.vectorSync.SyncObservation(ctx, obs); err != nil && s.ctx.Err() == nil {
|
||||
log.Warn().Err(err).Int64("id", id).Msg("Failed to restore an unarchived observation to sqlite-vec")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Invalidate all caches since we don't know the project
|
||||
s.invalidateAllObsCountCache()
|
||||
|
||||
@@ -400,9 +433,9 @@ func (s *Service) handleExportObservations(w http.ResponseWriter, r *http.Reques
|
||||
var err error
|
||||
|
||||
if project != "" {
|
||||
observations, _, err = s.observationStore.GetObservationsByProjectStrictPaginated(ctx, project, limit, 0)
|
||||
observations, _, err = s.observationStore.GetObservationsByProjectStrictPaginated(ctx, project, limit, 0, true)
|
||||
} else {
|
||||
observations, _, err = s.observationStore.GetAllRecentObservationsPaginated(ctx, limit, 0)
|
||||
observations, _, err = s.observationStore.GetAllRecentObservationsPaginated(ctx, limit, 0, true)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -531,14 +564,17 @@ func (s *Service) handleBulkStatusUpdate(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
case "archive":
|
||||
var archivedIDs []int64
|
||||
for _, id := range req.IDs {
|
||||
if err := s.observationStore.ArchiveObservation(ctx, id, req.Reason); err != nil {
|
||||
failed++
|
||||
errors = append(errors, fmt.Sprintf("id %d: %v", id, err))
|
||||
} else {
|
||||
updated++
|
||||
archivedIDs = append(archivedIDs, id)
|
||||
}
|
||||
}
|
||||
s.dropArchivedFromVectors(archivedIDs)
|
||||
|
||||
case "set_feedback":
|
||||
if req.Feedback < -1 || req.Feedback > 1 {
|
||||
@@ -598,9 +634,9 @@ func (s *Service) handleFindDuplicates(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
|
||||
if project != "" {
|
||||
observations, _, err = s.observationStore.GetObservationsByProjectStrictPaginated(ctx, project, limit, 0)
|
||||
observations, _, err = s.observationStore.GetObservationsByProjectStrictPaginated(ctx, project, limit, 0, true)
|
||||
} else {
|
||||
observations, _, err = s.observationStore.GetAllRecentObservationsPaginated(ctx, limit, 0)
|
||||
observations, _, err = s.observationStore.GetAllRecentObservationsPaginated(ctx, limit, 0, true)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
||||
+10
-10
@@ -13,7 +13,7 @@
|
||||
{
|
||||
"name": "claude-mnemonic",
|
||||
"description": "Persistent memory system for Claude Code - Go implementation with SQLite and ChromaDB vector search",
|
||||
"version": "0.21.119",
|
||||
"version": "0.21.131",
|
||||
"author": {
|
||||
"name": "lukaszraczylo",
|
||||
"url": "https://github.com/lukaszraczylo"
|
||||
@@ -23,25 +23,25 @@
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/lukaszraczylo/claude-mnemonic",
|
||||
"releases": {
|
||||
"latest": "0.21.119",
|
||||
"latest": "0.21.131",
|
||||
"versions": {
|
||||
"0.21.119": {
|
||||
"releaseDate": "2026-10-10",
|
||||
"0.21.131": {
|
||||
"releaseDate": "2026-10-13",
|
||||
"downloads": {
|
||||
"darwin-arm64": {
|
||||
"url": "https://github.com/lukaszraczylo/claude-mnemonic/releases/download/v0.21.119/claude-mnemonic_0.21.119_darwin_arm64.tar.gz",
|
||||
"url": "https://github.com/lukaszraczylo/claude-mnemonic/releases/download/v0.21.131/claude-mnemonic_0.21.131_darwin_arm64.tar.gz",
|
||||
"format": "tar.gz",
|
||||
"sha256": "50cf64483e7ee7f0ece80c336c916e6c60c4f3fc921073750564332860385c3d"
|
||||
"sha256": "615fc6566ec0f212a22b393462d3858d446aacbdcee00dad7112ef83087831d0"
|
||||
},
|
||||
"linux-amd64": {
|
||||
"url": "https://github.com/lukaszraczylo/claude-mnemonic/releases/download/v0.21.119/claude-mnemonic_0.21.119_linux_amd64.tar.gz",
|
||||
"url": "https://github.com/lukaszraczylo/claude-mnemonic/releases/download/v0.21.131/claude-mnemonic_0.21.131_linux_amd64.tar.gz",
|
||||
"format": "tar.gz",
|
||||
"sha256": "dd4cc6d99388cec9d6bbd9259bd6ca8c9d3b0fc7303c5ec332bec5187de399b3"
|
||||
"sha256": "fe095fa1afaf64a55f59d0c17f134764e3b70653d3ef12637e1fd22ba995f18f"
|
||||
},
|
||||
"windows-amd64": {
|
||||
"url": "https://github.com/lukaszraczylo/claude-mnemonic/releases/download/v0.21.119/claude-mnemonic_0.21.119_windows_amd64.zip",
|
||||
"url": "https://github.com/lukaszraczylo/claude-mnemonic/releases/download/v0.21.131/claude-mnemonic_0.21.131_windows_amd64.zip",
|
||||
"format": "zip",
|
||||
"sha256": "5147d89eed868366a51f5b0555b8552a3b75f996962bbb281a44361c76c3d73b"
|
||||
"sha256": "f699ef6bc0724b8b666b9cd87ff68b191da8ffd269ea8d93c701c42a368ae2ca"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+14
-14
@@ -802,9 +802,9 @@
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "26.6.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.4.tgz",
|
||||
"integrity": "sha512-ldVPDCzj7fsaGZrLB0NuHuTvJcsNasysBAqMolr/cgxrLd1xbqxIr3XJiPnHHJUCxj5sNF1vnRj9aWnrVh5Jcg==",
|
||||
"version": "26.6.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.5.tgz",
|
||||
"integrity": "sha512-uMzsv9stDdofIZBx4UGh4Z9ZgXnGC9seRDscilBcTHAeOFk67MZg/k9bWVfO+bk4qZmG+l52hRnLwUiH2LnJTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -812,9 +812,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitejs/plugin-vue": {
|
||||
"version": "6.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.9.tgz",
|
||||
"integrity": "sha512-rD/MORlhaZMlXWW0rEn4FB4wMinWC0z/D7Ye160S5+Rs1mC8ZDAdcDp4SjKfUhG5t8q1ktcPVw4xuTkPRlzrSA==",
|
||||
"version": "6.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.10.tgz",
|
||||
"integrity": "sha512-b80aUQh3n+qEzzZm5oQp3rT2QImeCx58K6eFl/RkJ3d65KW0kTuv5lNsLRVAjbW9YRUpo/wzmvzMgV34bTAItA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -825,7 +825,7 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0",
|
||||
"vue": "^3.2.25"
|
||||
"vue": "^3.2.25 || ^3.6.0-rc.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@volar/language-core": {
|
||||
@@ -908,9 +908,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/language-core": {
|
||||
"version": "3.3.12",
|
||||
"resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.3.12.tgz",
|
||||
"integrity": "sha512-k7uIKR0n4CBidk233y7cac9osnDQ5WRidTl6nsrNvpZ3nf8cx9iJxRVsRzVsoPRHbowT/iKgGMTS9RGov3jeIA==",
|
||||
"version": "3.3.13",
|
||||
"resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.3.13.tgz",
|
||||
"integrity": "sha512-axA0Xemn5LuUKuKWob0/kVq7xQ2mJJM6VqA9Exvt35nM9yRC31goWVve/G/Zb2ZCLsXC8FHYjjfXlnshknSCew==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -2003,14 +2003,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vue-tsc": {
|
||||
"version": "3.3.12",
|
||||
"resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.3.12.tgz",
|
||||
"integrity": "sha512-kbpaz18O9jPVkXVHD1BEjpUXM6WhXSu+62L1sGGr6Wbzd8GbvkBQWjoj3KZ/C0aq2MZllce7UdtWzwCIYsBMGA==",
|
||||
"version": "3.3.13",
|
||||
"resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.3.13.tgz",
|
||||
"integrity": "sha512-jE4/bXdOMSHVxEGgQCqNllJy+9N3Gx6tfSAsg0yhFK/MAN6eXqISihupnDEKSCt12VVnjN2gI3AqLYcqoh5x/A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@volar/typescript": "2.4.28",
|
||||
"@vue/language-core": "3.3.12"
|
||||
"@vue/language-core": "3.3.13"
|
||||
},
|
||||
"bin": {
|
||||
"vue-tsc": "bin/vue-tsc.js"
|
||||
|
||||
Reference in New Issue
Block a user