prjct-cli 0.20.0 → 0.21.0
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/CHANGELOG.md +24 -6
- package/CLAUDE.md +56 -15
- package/README.md +5 -6
- package/bin/prjct +59 -42
- package/bin/prjct.ts +60 -0
- package/core/__tests__/agentic/memory-system.test.ts +18 -3
- package/core/__tests__/agentic/plan-mode.test.ts +55 -26
- package/core/__tests__/agentic/prompt-builder.test.ts +6 -6
- package/core/__tests__/utils/project-commands.test.ts +72 -0
- package/core/agentic/agent-router.ts +3 -12
- package/core/agentic/command-executor.ts +372 -3
- package/core/agentic/context-builder.ts +7 -27
- package/core/agentic/ground-truth.ts +604 -5
- package/core/agentic/index.ts +180 -0
- package/core/agentic/loop-detector.ts +418 -4
- package/core/agentic/memory-system.ts +857 -3
- package/core/agentic/plan-mode.ts +491 -4
- package/core/agentic/prompt-builder.ts +44 -65
- package/core/agentic/services.ts +13 -5
- package/core/agentic/skill-loader.ts +112 -0
- package/core/agentic/smart-context.ts +37 -122
- package/core/agentic/template-loader.ts +79 -122
- package/core/agentic/tool-registry.ts +5 -11
- package/core/agents/index.ts +1 -1
- package/core/agents/performance.ts +4 -2
- package/core/bus/bus.ts +262 -0
- package/core/bus/index.ts +3 -313
- package/core/commands/analysis.ts +5 -5
- package/core/commands/analytics.ts +11 -11
- package/core/commands/base.ts +33 -209
- package/core/commands/cleanup.ts +148 -0
- package/core/commands/command-data.ts +346 -0
- package/core/commands/commands.ts +216 -0
- package/core/commands/design.ts +83 -0
- package/core/commands/index.ts +13 -207
- package/core/commands/maintenance.ts +52 -473
- package/core/commands/planning.ts +3 -3
- package/core/commands/register.ts +104 -0
- package/core/commands/registry.ts +441 -0
- package/core/commands/setup.ts +25 -9
- package/core/commands/shipping.ts +48 -11
- package/core/commands/snapshots.ts +299 -0
- package/core/commands/workflow.ts +2 -2
- package/core/constants/index.ts +254 -4
- package/core/domain/agent-loader.ts +5 -6
- package/core/domain/task-stack.ts +555 -4
- package/core/errors.ts +127 -1
- package/core/events/events.ts +87 -0
- package/core/events/index.ts +4 -138
- package/core/index.ts +15 -23
- package/core/infrastructure/agent-detector.ts +126 -201
- package/core/infrastructure/author-detector.ts +99 -171
- package/core/infrastructure/command-installer.ts +476 -4
- package/core/infrastructure/config-manager.ts +41 -37
- package/core/infrastructure/path-manager.ts +59 -9
- package/core/infrastructure/permission-manager.ts +286 -0
- package/core/integrations/notion/client.ts +323 -0
- package/core/integrations/notion/index.ts +43 -0
- package/core/integrations/notion/setup.ts +230 -0
- package/core/integrations/notion/sync.ts +311 -0
- package/core/integrations/notion/templates.ts +234 -0
- package/core/outcomes/analyzer.ts +7 -41
- package/core/outcomes/index.ts +1 -1
- package/core/outcomes/recorder.ts +1 -1
- package/core/plugin/builtin/notion.ts +178 -0
- package/core/{plugins → plugin/builtin}/webhook.ts +6 -22
- package/core/plugin/loader.ts +5 -5
- package/core/plugin/registry.ts +2 -2
- package/core/schemas/ideas.ts +85 -54
- package/core/schemas/index.ts +14 -33
- package/core/schemas/permissions.ts +177 -0
- package/core/schemas/project.ts +39 -12
- package/core/schemas/roadmap.ts +94 -59
- package/core/schemas/schemas.ts +39 -0
- package/core/schemas/shipped.ts +87 -60
- package/core/schemas/state.ts +110 -70
- package/core/server/index.ts +21 -0
- package/core/server/routes.ts +165 -0
- package/core/server/server.ts +136 -0
- package/core/server/sse.ts +135 -0
- package/core/services/agent-service.ts +170 -0
- package/core/services/breakdown-service.ts +126 -0
- package/core/services/index.ts +21 -0
- package/core/services/memory-service.ts +108 -0
- package/core/services/project-service.ts +146 -0
- package/core/services/skill-service.ts +253 -0
- package/core/session/compaction.ts +257 -0
- package/core/session/index.ts +20 -8
- package/core/{infrastructure/session-manager/migration.ts → session/log-migration.ts} +9 -9
- package/core/{infrastructure/session-manager/session-manager.ts → session/session-log-manager.ts} +27 -26
- package/core/session/{session-manager.ts → task-session-manager.ts} +7 -4
- package/core/session/utils.ts +1 -1
- package/core/storage/ideas-storage.ts +10 -26
- package/core/storage/index.ts +14 -162
- package/core/storage/queue-storage.ts +13 -11
- package/core/storage/shipped-storage.ts +4 -17
- package/core/storage/state-storage.ts +35 -43
- package/core/storage/storage-manager.ts +42 -52
- package/core/storage/storage.ts +160 -0
- package/core/sync/auth-config.ts +1 -8
- package/core/sync/index.ts +17 -10
- package/core/sync/oauth-handler.ts +1 -6
- package/core/sync/sync-client.ts +6 -34
- package/core/sync/sync-manager.ts +11 -40
- package/core/types/agentic.ts +577 -0
- package/core/types/agents.ts +145 -0
- package/core/types/bus.ts +82 -0
- package/core/types/commands.ts +366 -0
- package/core/types/config.ts +70 -0
- package/core/types/core.ts +96 -0
- package/core/types/domain.ts +71 -0
- package/core/types/events.ts +42 -0
- package/core/types/fs.ts +56 -0
- package/core/types/index.ts +396 -500
- package/core/types/infrastructure.ts +196 -0
- package/core/types/integrations.ts +57 -0
- package/core/{agentic/memory-system/types.ts → types/memory.ts} +33 -8
- package/core/{outcomes/types.ts → types/outcomes.ts} +53 -8
- package/core/types/plugin.ts +25 -0
- package/core/types/server.ts +54 -0
- package/core/types/services.ts +65 -0
- package/core/types/session.ts +135 -0
- package/core/types/storage.ts +148 -0
- package/core/types/sync.ts +121 -0
- package/core/types/task.ts +72 -0
- package/core/types/template.ts +24 -0
- package/core/types/utils.ts +90 -0
- package/core/utils/cache.ts +195 -0
- package/core/utils/collection-filters.ts +245 -0
- package/core/utils/date-helper.ts +1 -5
- package/core/utils/file-helper.ts +20 -10
- package/core/utils/jsonl-helper.ts +5 -8
- package/core/utils/markdown-builder.ts +277 -0
- package/core/utils/project-commands.ts +132 -0
- package/core/utils/runtime.ts +119 -0
- package/dist/bin/prjct.mjs +12568 -0
- package/package.json +13 -8
- package/scripts/build.js +106 -0
- package/scripts/postinstall.js +50 -8
- package/templates/agentic/subagent-generation.md +1 -1
- package/templates/commands/init.md +43 -0
- package/templates/commands/notion-setup.md +191 -0
- package/templates/commands/serve.md +118 -0
- package/templates/commands/ship.md +13 -2
- package/templates/commands/skill.md +110 -0
- package/templates/commands/sync.md +1 -1
- package/templates/commands/test.md +23 -4
- package/templates/mcp-config.json +28 -0
- package/templates/permissions/default.jsonc +60 -0
- package/templates/permissions/permissive.jsonc +49 -0
- package/templates/permissions/strict.jsonc +62 -0
- package/templates/skills/code-review.md +47 -0
- package/templates/skills/debug.md +61 -0
- package/templates/skills/refactor.md +47 -0
- package/templates/subagents/domain/devops.md +1 -1
- package/templates/subagents/domain/testing.md +6 -10
- package/templates/subagents/workflow/prjct-shipper.md +16 -7
- package/templates/tools/bash.txt +22 -0
- package/templates/tools/edit.txt +18 -0
- package/templates/tools/glob.txt +19 -0
- package/templates/tools/grep.txt +21 -0
- package/templates/tools/read.txt +14 -0
- package/templates/tools/task.txt +20 -0
- package/templates/tools/webfetch.txt +16 -0
- package/templates/tools/websearch.txt +18 -0
- package/templates/tools/write.txt +17 -0
- package/core/agentic/command-executor/command-executor.ts +0 -312
- package/core/agentic/command-executor/index.ts +0 -16
- package/core/agentic/command-executor/status-signal.ts +0 -38
- package/core/agentic/command-executor/types.ts +0 -79
- package/core/agentic/ground-truth/index.ts +0 -76
- package/core/agentic/ground-truth/types.ts +0 -33
- package/core/agentic/ground-truth/utils.ts +0 -48
- package/core/agentic/ground-truth/verifiers/analyze.ts +0 -54
- package/core/agentic/ground-truth/verifiers/done.ts +0 -75
- package/core/agentic/ground-truth/verifiers/feature.ts +0 -70
- package/core/agentic/ground-truth/verifiers/index.ts +0 -37
- package/core/agentic/ground-truth/verifiers/init.ts +0 -52
- package/core/agentic/ground-truth/verifiers/now.ts +0 -57
- package/core/agentic/ground-truth/verifiers/ship.ts +0 -85
- package/core/agentic/ground-truth/verifiers/spec.ts +0 -45
- package/core/agentic/ground-truth/verifiers/sync.ts +0 -47
- package/core/agentic/ground-truth/verifiers.ts +0 -6
- package/core/agentic/loop-detector/error-analysis.ts +0 -97
- package/core/agentic/loop-detector/hallucination.ts +0 -71
- package/core/agentic/loop-detector/index.ts +0 -41
- package/core/agentic/loop-detector/loop-detector.ts +0 -222
- package/core/agentic/loop-detector/types.ts +0 -66
- package/core/agentic/memory-system/history.ts +0 -53
- package/core/agentic/memory-system/index.ts +0 -192
- package/core/agentic/memory-system/patterns.ts +0 -156
- package/core/agentic/memory-system/semantic-memories.ts +0 -278
- package/core/agentic/memory-system/session.ts +0 -21
- package/core/agentic/plan-mode/approval.ts +0 -57
- package/core/agentic/plan-mode/constants.ts +0 -44
- package/core/agentic/plan-mode/index.ts +0 -28
- package/core/agentic/plan-mode/plan-mode.ts +0 -407
- package/core/agentic/plan-mode/types.ts +0 -193
- package/core/agents/types.ts +0 -126
- package/core/command-registry/categories.ts +0 -23
- package/core/command-registry/commands.ts +0 -15
- package/core/command-registry/core-commands.ts +0 -344
- package/core/command-registry/index.ts +0 -158
- package/core/command-registry/optional-commands.ts +0 -163
- package/core/command-registry/setup-commands.ts +0 -83
- package/core/command-registry/types.ts +0 -59
- package/core/command-registry.ts +0 -9
- package/core/commands/types.ts +0 -185
- package/core/commands.ts +0 -11
- package/core/constants/formats.ts +0 -187
- package/core/context-sync.ts +0 -18
- package/core/data/index.ts +0 -27
- package/core/data/md-base-manager.ts +0 -203
- package/core/data/md-ideas-manager.ts +0 -155
- package/core/data/md-queue-manager.ts +0 -180
- package/core/data/md-shipped-manager.ts +0 -90
- package/core/data/md-state-manager.ts +0 -137
- package/core/domain/task-stack/index.ts +0 -19
- package/core/domain/task-stack/parser.ts +0 -86
- package/core/domain/task-stack/storage.ts +0 -123
- package/core/domain/task-stack/task-stack.ts +0 -340
- package/core/domain/task-stack/types.ts +0 -51
- package/core/infrastructure/command-installer/command-installer.ts +0 -327
- package/core/infrastructure/command-installer/global-config.ts +0 -136
- package/core/infrastructure/command-installer/index.ts +0 -25
- package/core/infrastructure/command-installer/types.ts +0 -41
- package/core/infrastructure/session-manager/index.ts +0 -23
- package/core/infrastructure/session-manager/types.ts +0 -45
- package/core/infrastructure/session-manager.ts +0 -8
- package/core/serializers/ideas-serializer.ts +0 -187
- package/core/serializers/index.ts +0 -36
- package/core/serializers/queue-serializer.ts +0 -210
- package/core/serializers/shipped-serializer.ts +0 -108
- package/core/serializers/state-serializer.ts +0 -136
- package/core/session/types.ts +0 -29
- /package/core/infrastructure/{agents/claude-agent.ts → claude-agent.ts} +0 -0
|
@@ -1,8 +1,862 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Memory System
|
|
3
|
-
*
|
|
3
|
+
* Tracks user preferences, decisions, and learned patterns.
|
|
4
|
+
*
|
|
5
|
+
* Three-tier memory system:
|
|
6
|
+
* - Tier 1: Session (ephemeral) - single command context
|
|
7
|
+
* - Tier 2: Patterns (persistent) - learned preferences and decisions
|
|
8
|
+
* - Tier 3: History (JSONL) - append-only audit log
|
|
9
|
+
*
|
|
10
|
+
* @module agentic/memory-system
|
|
11
|
+
* @version 3.3
|
|
4
12
|
*/
|
|
5
13
|
|
|
6
|
-
import
|
|
14
|
+
import fs from 'fs/promises'
|
|
15
|
+
import path from 'path'
|
|
16
|
+
import pathManager from '../infrastructure/path-manager'
|
|
17
|
+
import { isNotFoundError } from '../types/fs'
|
|
18
|
+
import { generateUUID } from '../schemas'
|
|
19
|
+
import { getTimestamp, getTodayKey } from '../utils/date-helper'
|
|
20
|
+
import { appendJsonLine, getLastJsonLines } from '../utils/jsonl-helper'
|
|
21
|
+
import { ensureDir } from '../utils/file-helper'
|
|
22
|
+
|
|
23
|
+
// Re-export types from canonical location
|
|
24
|
+
export type {
|
|
25
|
+
Memory,
|
|
26
|
+
MemoryTag,
|
|
27
|
+
MemoryDatabase,
|
|
28
|
+
HistoryEntry,
|
|
29
|
+
HistoryEventType,
|
|
30
|
+
Decision,
|
|
31
|
+
Workflow,
|
|
32
|
+
Preference,
|
|
33
|
+
Patterns,
|
|
34
|
+
MemoryContext,
|
|
35
|
+
MemoryContextParams,
|
|
36
|
+
} from '../types/memory'
|
|
37
|
+
|
|
38
|
+
export { MEMORY_TAGS } from '../types/memory'
|
|
39
|
+
|
|
40
|
+
import type {
|
|
41
|
+
Memory,
|
|
42
|
+
MemoryTag,
|
|
43
|
+
MemoryDatabase,
|
|
44
|
+
HistoryEntry,
|
|
45
|
+
HistoryEventType,
|
|
46
|
+
Decision,
|
|
47
|
+
Workflow,
|
|
48
|
+
Preference,
|
|
49
|
+
Patterns,
|
|
50
|
+
MemoryContext,
|
|
51
|
+
} from '../types/memory'
|
|
52
|
+
|
|
53
|
+
import { MEMORY_TAGS } from '../types/memory'
|
|
54
|
+
|
|
55
|
+
// =============================================================================
|
|
56
|
+
// Base Store
|
|
57
|
+
// =============================================================================
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* CachedStore - Abstract base class for memory system stores
|
|
61
|
+
*
|
|
62
|
+
* Eliminates duplicated cache/load/save patterns across:
|
|
63
|
+
* - PatternStore (~40 lines of boilerplate)
|
|
64
|
+
* - SemanticMemories (~40 lines of boilerplate)
|
|
65
|
+
*
|
|
66
|
+
* Provides:
|
|
67
|
+
* - Lazy loading with project-scoped cache
|
|
68
|
+
* - Automatic directory creation on save
|
|
69
|
+
* - Reset functionality
|
|
70
|
+
* - Path management via pathManager
|
|
71
|
+
*/
|
|
72
|
+
export abstract class CachedStore<T> {
|
|
73
|
+
private _data: T | null = null
|
|
74
|
+
private _loaded: boolean = false
|
|
75
|
+
private _projectId: string | null = null
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Get the filename for this store (e.g., 'patterns.json', 'memories.json')
|
|
79
|
+
*/
|
|
80
|
+
protected abstract getFilename(): string
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Get default data structure when file doesn't exist
|
|
84
|
+
*/
|
|
85
|
+
protected abstract getDefault(): T
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Optional: subdirectory within memory folder
|
|
89
|
+
*/
|
|
90
|
+
protected getSubdirectory(): string | null {
|
|
91
|
+
return null
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Get full path for the store file
|
|
96
|
+
*/
|
|
97
|
+
protected getPath(projectId: string): string {
|
|
98
|
+
const basePath = path.join(pathManager.getGlobalProjectPath(projectId), 'memory')
|
|
99
|
+
|
|
100
|
+
const subdir = this.getSubdirectory()
|
|
101
|
+
if (subdir) {
|
|
102
|
+
return path.join(basePath, subdir, this.getFilename())
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return path.join(basePath, this.getFilename())
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Load data from disk (with caching)
|
|
110
|
+
* Returns cached data if same project and already loaded
|
|
111
|
+
*/
|
|
112
|
+
async load(projectId: string): Promise<T> {
|
|
113
|
+
// Return cached if same project and loaded
|
|
114
|
+
if (this._loaded && this._data && this._projectId === projectId) {
|
|
115
|
+
return this._data
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Load from disk
|
|
119
|
+
const filePath = this.getPath(projectId)
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
const content = await fs.readFile(filePath, 'utf-8')
|
|
123
|
+
this._data = JSON.parse(content) as T
|
|
124
|
+
// Allow subclasses to normalize data after load
|
|
125
|
+
this.afterLoad(this._data)
|
|
126
|
+
} catch (error) {
|
|
127
|
+
if (isNotFoundError(error)) {
|
|
128
|
+
this._data = this.getDefault()
|
|
129
|
+
} else {
|
|
130
|
+
throw error
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
this._loaded = true
|
|
135
|
+
this._projectId = projectId
|
|
136
|
+
|
|
137
|
+
return this._data
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Hook for subclasses to normalize data after loading
|
|
142
|
+
* E.g., ensuring all index keys exist
|
|
143
|
+
*/
|
|
144
|
+
protected afterLoad(_data: T): void {
|
|
145
|
+
// Override in subclass if needed
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Save data to disk
|
|
150
|
+
*/
|
|
151
|
+
async save(projectId: string): Promise<void> {
|
|
152
|
+
if (!this._data) return
|
|
153
|
+
|
|
154
|
+
const filePath = this.getPath(projectId)
|
|
155
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true })
|
|
156
|
+
await fs.writeFile(filePath, JSON.stringify(this._data, null, 2), 'utf-8')
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Get cached data without loading (may be null)
|
|
161
|
+
*/
|
|
162
|
+
protected getData(): T | null {
|
|
163
|
+
return this._data
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Set data directly (for subclass modifications)
|
|
168
|
+
*/
|
|
169
|
+
protected setData(data: T): void {
|
|
170
|
+
this._data = data
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Update data with a transform function, then save
|
|
175
|
+
*/
|
|
176
|
+
async update(projectId: string, updater: (data: T) => T): Promise<T> {
|
|
177
|
+
const data = await this.load(projectId)
|
|
178
|
+
const updated = updater(data)
|
|
179
|
+
this._data = updated
|
|
180
|
+
await this.save(projectId)
|
|
181
|
+
return updated
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Check if data has been loaded for a project
|
|
186
|
+
*/
|
|
187
|
+
isLoaded(projectId?: string): boolean {
|
|
188
|
+
if (projectId) {
|
|
189
|
+
return this._loaded && this._projectId === projectId
|
|
190
|
+
}
|
|
191
|
+
return this._loaded
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Reset cache (forces reload on next access)
|
|
196
|
+
*/
|
|
197
|
+
reset(): void {
|
|
198
|
+
this._data = null
|
|
199
|
+
this._loaded = false
|
|
200
|
+
this._projectId = null
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// =============================================================================
|
|
205
|
+
// Session Store (Tier 1)
|
|
206
|
+
// =============================================================================
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Session Memory - Tier 1
|
|
210
|
+
* Ephemeral, single command context.
|
|
211
|
+
*/
|
|
212
|
+
export class SessionStore {
|
|
213
|
+
private _sessionMemory: Map<string, { value: unknown; timestamp: number }> = new Map()
|
|
214
|
+
|
|
215
|
+
setSession(key: string, value: unknown): void {
|
|
216
|
+
this._sessionMemory.set(key, { value, timestamp: Date.now() })
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
getSession(key: string): unknown {
|
|
220
|
+
const entry = this._sessionMemory.get(key)
|
|
221
|
+
return entry?.value
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
clearSession(): void {
|
|
225
|
+
this._sessionMemory.clear()
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// =============================================================================
|
|
230
|
+
// History Store (Tier 3)
|
|
231
|
+
// =============================================================================
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* History - Tier 3
|
|
235
|
+
* Append-only JSONL audit log with temporal fragmentation.
|
|
236
|
+
*/
|
|
237
|
+
export class HistoryStore {
|
|
238
|
+
private _getSessionPath(projectId: string): string {
|
|
239
|
+
const now = new Date()
|
|
240
|
+
const yearMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`
|
|
241
|
+
const day = getTodayKey()
|
|
242
|
+
|
|
243
|
+
return path.join(pathManager.getGlobalProjectPath(projectId), 'memory', 'sessions', yearMonth, `${day}.jsonl`)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async appendHistory(projectId: string, entry: Record<string, unknown> & { type: HistoryEventType }): Promise<void> {
|
|
247
|
+
const sessionPath = this._getSessionPath(projectId)
|
|
248
|
+
await ensureDir(path.dirname(sessionPath))
|
|
249
|
+
|
|
250
|
+
const logEntry: HistoryEntry = {
|
|
251
|
+
ts: getTimestamp(),
|
|
252
|
+
...entry,
|
|
253
|
+
type: entry.type,
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
await appendJsonLine(sessionPath, logEntry)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
async getRecentHistory(projectId: string, limit: number = 20): Promise<HistoryEntry[]> {
|
|
260
|
+
const sessionPath = this._getSessionPath(projectId)
|
|
261
|
+
return getLastJsonLines<HistoryEntry>(sessionPath, limit)
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// =============================================================================
|
|
266
|
+
// Pattern Store (Tier 2)
|
|
267
|
+
// =============================================================================
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Patterns - Tier 2
|
|
271
|
+
* Persistent learned preferences and decisions.
|
|
272
|
+
*/
|
|
273
|
+
export class PatternStore extends CachedStore<Patterns> {
|
|
274
|
+
protected getFilename(): string {
|
|
275
|
+
return 'patterns.json'
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
protected getDefault(): Patterns {
|
|
279
|
+
return {
|
|
280
|
+
version: 1,
|
|
281
|
+
decisions: {},
|
|
282
|
+
preferences: {},
|
|
283
|
+
workflows: {},
|
|
284
|
+
counters: {},
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Convenience alias for backward compatibility
|
|
289
|
+
async loadPatterns(projectId: string): Promise<Patterns> {
|
|
290
|
+
return this.load(projectId)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
async savePatterns(projectId: string): Promise<void> {
|
|
294
|
+
return this.save(projectId)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
async recordDecision(projectId: string, key: string, value: string, context: string = ''): Promise<void> {
|
|
298
|
+
const patterns = await this.load(projectId)
|
|
299
|
+
const now = getTimestamp()
|
|
300
|
+
|
|
301
|
+
if (!patterns.decisions[key]) {
|
|
302
|
+
patterns.decisions[key] = {
|
|
303
|
+
value,
|
|
304
|
+
count: 1,
|
|
305
|
+
firstSeen: now,
|
|
306
|
+
lastSeen: now,
|
|
307
|
+
confidence: 'low',
|
|
308
|
+
contexts: [context].filter(Boolean),
|
|
309
|
+
}
|
|
310
|
+
} else {
|
|
311
|
+
const decision = patterns.decisions[key]
|
|
312
|
+
|
|
313
|
+
if (decision.value === value) {
|
|
314
|
+
decision.count++
|
|
315
|
+
decision.lastSeen = now
|
|
316
|
+
if (context && !decision.contexts.includes(context)) {
|
|
317
|
+
decision.contexts.push(context)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (decision.count >= 5) {
|
|
321
|
+
decision.confidence = 'high'
|
|
322
|
+
} else if (decision.count >= 3) {
|
|
323
|
+
decision.confidence = 'medium'
|
|
324
|
+
}
|
|
325
|
+
} else {
|
|
326
|
+
decision.value = value
|
|
327
|
+
decision.count = 1
|
|
328
|
+
decision.lastSeen = now
|
|
329
|
+
decision.confidence = 'low'
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
await this.save(projectId)
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
async getDecision(projectId: string, key: string): Promise<{ value: string; confidence: string } | null> {
|
|
337
|
+
const patterns = await this.load(projectId)
|
|
338
|
+
const decision = patterns.decisions[key]
|
|
339
|
+
|
|
340
|
+
if (!decision) return null
|
|
341
|
+
if (decision.confidence === 'low') return null
|
|
342
|
+
|
|
343
|
+
return { value: decision.value, confidence: decision.confidence }
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
async hasPattern(projectId: string, key: string): Promise<boolean> {
|
|
347
|
+
const decision = await this.getDecision(projectId, key)
|
|
348
|
+
return decision !== null
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
async recordWorkflow(projectId: string, workflowName: string, pattern: Record<string, unknown>): Promise<void> {
|
|
352
|
+
const patterns = await this.load(projectId)
|
|
353
|
+
const now = getTimestamp()
|
|
354
|
+
|
|
355
|
+
if (!patterns.workflows[workflowName]) {
|
|
356
|
+
patterns.workflows[workflowName] = {
|
|
357
|
+
...pattern,
|
|
358
|
+
count: 1,
|
|
359
|
+
firstSeen: now,
|
|
360
|
+
lastSeen: now,
|
|
361
|
+
}
|
|
362
|
+
} else {
|
|
363
|
+
patterns.workflows[workflowName].count++
|
|
364
|
+
patterns.workflows[workflowName].lastSeen = now
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
await this.save(projectId)
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
async getWorkflow(projectId: string, workflowName: string): Promise<Workflow | null> {
|
|
371
|
+
const patterns = await this.load(projectId)
|
|
372
|
+
const workflow = patterns.workflows[workflowName]
|
|
373
|
+
|
|
374
|
+
if (!workflow || workflow.count < 3) return null
|
|
375
|
+
return workflow
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
async setPreference(projectId: string, key: string, value: Preference['value']): Promise<void> {
|
|
379
|
+
const patterns = await this.load(projectId)
|
|
380
|
+
patterns.preferences[key] = { value, updatedAt: getTimestamp() }
|
|
381
|
+
await this.save(projectId)
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
async getPreference(projectId: string, key: string, defaultValue: unknown = null): Promise<unknown> {
|
|
385
|
+
const patterns = await this.load(projectId)
|
|
386
|
+
return patterns.preferences[key]?.value ?? defaultValue
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
async getPatternsSummary(projectId: string) {
|
|
390
|
+
const patterns = await this.load(projectId)
|
|
391
|
+
|
|
392
|
+
return {
|
|
393
|
+
decisions: Object.keys(patterns.decisions).length,
|
|
394
|
+
learnedDecisions: Object.values(patterns.decisions).filter((d) => d.confidence !== 'low').length,
|
|
395
|
+
workflows: Object.keys(patterns.workflows).length,
|
|
396
|
+
preferences: Object.keys(patterns.preferences).length,
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// =============================================================================
|
|
402
|
+
// Semantic Memories
|
|
403
|
+
// =============================================================================
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Semantic Memories
|
|
407
|
+
* P3.3: Tagged, searchable, CRUD memory operations.
|
|
408
|
+
*/
|
|
409
|
+
export class SemanticMemories extends CachedStore<MemoryDatabase> {
|
|
410
|
+
protected getFilename(): string {
|
|
411
|
+
return 'memories.json'
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
protected getDefault(): MemoryDatabase {
|
|
415
|
+
return {
|
|
416
|
+
version: 1,
|
|
417
|
+
memories: [],
|
|
418
|
+
index: this._createEmptyIndex(),
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
protected afterLoad(db: MemoryDatabase): void {
|
|
423
|
+
this._normalizeIndex(db)
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
private _createEmptyIndex(): Record<string, string[]> {
|
|
427
|
+
const tags = Object.values(MEMORY_TAGS)
|
|
428
|
+
const index: Record<string, string[]> = {}
|
|
429
|
+
for (const tag of tags) index[tag] = []
|
|
430
|
+
return index
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
private _normalizeIndex(db: MemoryDatabase): void {
|
|
434
|
+
// Reason: older persisted files may not include newer tags; ensure all tags are present.
|
|
435
|
+
const tags = Object.values(MEMORY_TAGS)
|
|
436
|
+
for (const tag of tags) {
|
|
437
|
+
if (!db.index[tag]) db.index[tag] = []
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
private _coerceTags(tags: string[]): MemoryTag[] {
|
|
442
|
+
const allowed = new Set<MemoryTag>(Object.values(MEMORY_TAGS) as MemoryTag[])
|
|
443
|
+
return tags.filter((t): t is MemoryTag => allowed.has(t as MemoryTag))
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// Convenience alias for backward compatibility
|
|
447
|
+
async loadMemories(projectId: string): Promise<MemoryDatabase> {
|
|
448
|
+
return this.load(projectId)
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
async saveMemories(projectId: string): Promise<void> {
|
|
452
|
+
return this.save(projectId)
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
async createMemory(
|
|
456
|
+
projectId: string,
|
|
457
|
+
{
|
|
458
|
+
title,
|
|
459
|
+
content,
|
|
460
|
+
tags = [],
|
|
461
|
+
userTriggered = false,
|
|
462
|
+
}: { title: string; content: string; tags?: string[]; userTriggered?: boolean }
|
|
463
|
+
): Promise<string> {
|
|
464
|
+
const db = await this.load(projectId)
|
|
465
|
+
const parsedTags = this._coerceTags(tags)
|
|
466
|
+
const now = getTimestamp()
|
|
467
|
+
|
|
468
|
+
const memory: Memory = {
|
|
469
|
+
id: generateUUID(),
|
|
470
|
+
title,
|
|
471
|
+
content,
|
|
472
|
+
tags: parsedTags,
|
|
473
|
+
userTriggered,
|
|
474
|
+
createdAt: now,
|
|
475
|
+
updatedAt: now,
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
db.memories.push(memory)
|
|
479
|
+
|
|
480
|
+
for (const tag of parsedTags) {
|
|
481
|
+
db.index[tag].push(memory.id)
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
await this.save(projectId)
|
|
485
|
+
return memory.id
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
async updateMemory(
|
|
489
|
+
projectId: string,
|
|
490
|
+
memoryId: string,
|
|
491
|
+
updates: { title?: string; content?: string; tags?: string[] }
|
|
492
|
+
): Promise<boolean> {
|
|
493
|
+
const db = await this.load(projectId)
|
|
494
|
+
|
|
495
|
+
const index = db.memories.findIndex((m) => m.id === memoryId)
|
|
496
|
+
if (index === -1) return false
|
|
497
|
+
|
|
498
|
+
const memory = db.memories[index]
|
|
499
|
+
const oldTags = memory.tags || []
|
|
500
|
+
|
|
501
|
+
if (updates.title) memory.title = updates.title
|
|
502
|
+
if (updates.content) memory.content = updates.content
|
|
503
|
+
if (updates.tags) {
|
|
504
|
+
const newTags = this._coerceTags(updates.tags)
|
|
505
|
+
for (const tag of oldTags) {
|
|
506
|
+
db.index[tag] = db.index[tag].filter((id: string) => id !== memoryId)
|
|
507
|
+
}
|
|
508
|
+
for (const tag of newTags) {
|
|
509
|
+
db.index[tag].push(memoryId)
|
|
510
|
+
}
|
|
511
|
+
memory.tags = newTags
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
memory.updatedAt = getTimestamp()
|
|
515
|
+
await this.save(projectId)
|
|
516
|
+
return true
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
async deleteMemory(projectId: string, memoryId: string): Promise<boolean> {
|
|
520
|
+
const db = await this.load(projectId)
|
|
521
|
+
|
|
522
|
+
const index = db.memories.findIndex((m) => m.id === memoryId)
|
|
523
|
+
if (index === -1) return false
|
|
524
|
+
|
|
525
|
+
const memory = db.memories[index]
|
|
526
|
+
|
|
527
|
+
for (const tag of memory.tags || []) {
|
|
528
|
+
if (db.index[tag]) {
|
|
529
|
+
db.index[tag] = db.index[tag].filter((id) => id !== memoryId)
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
db.memories.splice(index, 1)
|
|
534
|
+
await this.save(projectId)
|
|
535
|
+
return true
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
async findByTags(projectId: string, tags: string[], matchAll: boolean = false): Promise<Memory[]> {
|
|
539
|
+
const db = await this.load(projectId)
|
|
540
|
+
const parsedTags = this._coerceTags(tags)
|
|
541
|
+
|
|
542
|
+
if (matchAll) {
|
|
543
|
+
return db.memories.filter((m) => parsedTags.every((tag) => (m.tags || []).includes(tag)))
|
|
544
|
+
} else {
|
|
545
|
+
const matchingIds = new Set<string>()
|
|
546
|
+
for (const tag of parsedTags) {
|
|
547
|
+
const ids = db.index[tag]
|
|
548
|
+
ids.forEach((id: string) => matchingIds.add(id))
|
|
549
|
+
}
|
|
550
|
+
return db.memories.filter((m) => matchingIds.has(m.id))
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
async searchMemories(projectId: string, query: string): Promise<Memory[]> {
|
|
555
|
+
const db = await this.load(projectId)
|
|
556
|
+
const queryLower = query.toLowerCase()
|
|
557
|
+
|
|
558
|
+
return db.memories.filter(
|
|
559
|
+
(m) => m.title.toLowerCase().includes(queryLower) || m.content.toLowerCase().includes(queryLower)
|
|
560
|
+
)
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
async getRelevantMemories(projectId: string, context: MemoryContext, limit: number = 5): Promise<Memory[]> {
|
|
564
|
+
const db = await this.load(projectId)
|
|
565
|
+
|
|
566
|
+
const scored = db.memories.map((memory) => {
|
|
567
|
+
let score = 0
|
|
568
|
+
|
|
569
|
+
const contextTags = this._extractContextTags(context)
|
|
570
|
+
for (const tag of memory.tags || []) {
|
|
571
|
+
if (contextTags.includes(tag)) score += 10
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
const age = Date.now() - new Date(memory.updatedAt).getTime()
|
|
575
|
+
const daysSinceUpdate = age / (1000 * 60 * 60 * 24)
|
|
576
|
+
score += Math.max(0, 5 - daysSinceUpdate)
|
|
577
|
+
|
|
578
|
+
if (memory.userTriggered) score += 5
|
|
579
|
+
|
|
580
|
+
const keywords = this._extractKeywords(context)
|
|
581
|
+
for (const keyword of keywords) {
|
|
582
|
+
if (memory.content.toLowerCase().includes(keyword)) score += 2
|
|
583
|
+
if (memory.title.toLowerCase().includes(keyword)) score += 3
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
return { ...memory, _score: score }
|
|
587
|
+
})
|
|
588
|
+
|
|
589
|
+
return scored
|
|
590
|
+
.filter((m) => m._score > 0)
|
|
591
|
+
.sort((a, b) => b._score - a._score)
|
|
592
|
+
.slice(0, limit)
|
|
593
|
+
.map(({ _score, ...memory }) => memory as Memory)
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
private _extractContextTags(context: MemoryContext): string[] {
|
|
597
|
+
const tags: string[] = []
|
|
598
|
+
|
|
599
|
+
const commandTags: Record<string, string[]> = {
|
|
600
|
+
ship: [MEMORY_TAGS.COMMIT_STYLE, MEMORY_TAGS.SHIP_WORKFLOW, MEMORY_TAGS.TEST_BEHAVIOR],
|
|
601
|
+
feature: [MEMORY_TAGS.ARCHITECTURE, MEMORY_TAGS.CODE_STYLE],
|
|
602
|
+
done: [MEMORY_TAGS.SHIP_WORKFLOW],
|
|
603
|
+
analyze: [MEMORY_TAGS.TECH_STACK, MEMORY_TAGS.ARCHITECTURE],
|
|
604
|
+
spec: [MEMORY_TAGS.ARCHITECTURE, MEMORY_TAGS.CODE_STYLE],
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
if (context.commandName && commandTags[context.commandName]) {
|
|
608
|
+
tags.push(...commandTags[context.commandName])
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
return tags
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
private _extractKeywords(context: MemoryContext): string[] {
|
|
615
|
+
const keywords: string[] = []
|
|
616
|
+
|
|
617
|
+
if (context.params?.description) {
|
|
618
|
+
keywords.push(...(context.params.description as string).toLowerCase().split(/\s+/))
|
|
619
|
+
}
|
|
620
|
+
if (context.params?.feature) {
|
|
621
|
+
keywords.push(...(context.params.feature as string).toLowerCase().split(/\s+/))
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
const stopWords = ['the', 'a', 'an', 'is', 'are', 'to', 'for', 'and', 'or', 'in']
|
|
625
|
+
return keywords.filter((k) => k.length > 2 && !stopWords.includes(k))
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
async autoRemember(projectId: string, decisionType: string, value: string, context: string = ''): Promise<void> {
|
|
629
|
+
const tagMap: Record<string, string[]> = {
|
|
630
|
+
commit_footer: [MEMORY_TAGS.COMMIT_STYLE],
|
|
631
|
+
branch_naming: [MEMORY_TAGS.BRANCH_NAMING],
|
|
632
|
+
test_before_ship: [MEMORY_TAGS.TEST_BEHAVIOR, MEMORY_TAGS.SHIP_WORKFLOW],
|
|
633
|
+
preferred_agent: [MEMORY_TAGS.AGENT_PREFERENCE],
|
|
634
|
+
code_style: [MEMORY_TAGS.CODE_STYLE],
|
|
635
|
+
verbosity: [MEMORY_TAGS.OUTPUT_VERBOSITY],
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
const tags = tagMap[decisionType] || []
|
|
639
|
+
|
|
640
|
+
const existing = await this.searchMemories(projectId, decisionType)
|
|
641
|
+
if (existing.length > 0) {
|
|
642
|
+
await this.updateMemory(projectId, existing[0].id, {
|
|
643
|
+
content: `${decisionType}: ${value}`,
|
|
644
|
+
tags,
|
|
645
|
+
})
|
|
646
|
+
} else {
|
|
647
|
+
await this.createMemory(projectId, {
|
|
648
|
+
title: `Preference: ${decisionType}`,
|
|
649
|
+
content: `${decisionType}: ${value}${context ? `\nContext: ${context}` : ''}`,
|
|
650
|
+
tags,
|
|
651
|
+
userTriggered: true,
|
|
652
|
+
})
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
async getAllMemories(projectId: string): Promise<Memory[]> {
|
|
657
|
+
const db = await this.load(projectId)
|
|
658
|
+
return db.memories
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
async getMemoryStats(projectId: string) {
|
|
662
|
+
const db = await this.load(projectId)
|
|
663
|
+
|
|
664
|
+
const tagCounts: Record<string, number> = {}
|
|
665
|
+
for (const [tag, ids] of Object.entries(db.index)) {
|
|
666
|
+
tagCounts[tag] = ids.length
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
return {
|
|
670
|
+
totalMemories: db.memories.length,
|
|
671
|
+
userTriggered: db.memories.filter((m) => m.userTriggered).length,
|
|
672
|
+
tagCounts,
|
|
673
|
+
oldestMemory: db.memories[0]?.createdAt,
|
|
674
|
+
newestMemory: db.memories[db.memories.length - 1]?.createdAt,
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// =============================================================================
|
|
680
|
+
// Memory System (Main Class)
|
|
681
|
+
// =============================================================================
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* Three-tier memory system for learning user patterns.
|
|
685
|
+
* Tier 1: Session (ephemeral), Tier 2: Patterns (persistent), Tier 3: History (JSONL)
|
|
686
|
+
*/
|
|
687
|
+
export class MemorySystem {
|
|
688
|
+
private _semanticMemories: SemanticMemories
|
|
689
|
+
private _patternStore: PatternStore
|
|
690
|
+
private _historyStore: HistoryStore
|
|
691
|
+
private _sessionStore: SessionStore
|
|
692
|
+
|
|
693
|
+
constructor() {
|
|
694
|
+
this._semanticMemories = new SemanticMemories()
|
|
695
|
+
this._patternStore = new PatternStore()
|
|
696
|
+
this._historyStore = new HistoryStore()
|
|
697
|
+
this._sessionStore = new SessionStore()
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// ===========================================================================
|
|
701
|
+
// P3.3: SEMANTIC MEMORIES
|
|
702
|
+
// ===========================================================================
|
|
703
|
+
|
|
704
|
+
loadMemories(projectId: string) {
|
|
705
|
+
return this._semanticMemories.loadMemories(projectId)
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
saveMemories(projectId: string) {
|
|
709
|
+
return this._semanticMemories.saveMemories(projectId)
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
createMemory(
|
|
713
|
+
projectId: string,
|
|
714
|
+
options: { title: string; content: string; tags?: string[]; userTriggered?: boolean }
|
|
715
|
+
): Promise<string> {
|
|
716
|
+
return this._semanticMemories.createMemory(projectId, options)
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
updateMemory(
|
|
720
|
+
projectId: string,
|
|
721
|
+
memoryId: string,
|
|
722
|
+
updates: { title?: string; content?: string; tags?: string[] }
|
|
723
|
+
): Promise<boolean> {
|
|
724
|
+
return this._semanticMemories.updateMemory(projectId, memoryId, updates)
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
deleteMemory(projectId: string, memoryId: string): Promise<boolean> {
|
|
728
|
+
return this._semanticMemories.deleteMemory(projectId, memoryId)
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
findByTags(projectId: string, tags: string[], matchAll?: boolean): Promise<Memory[]> {
|
|
732
|
+
return this._semanticMemories.findByTags(projectId, tags, matchAll)
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
searchMemories(projectId: string, query: string): Promise<Memory[]> {
|
|
736
|
+
return this._semanticMemories.searchMemories(projectId, query)
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
getRelevantMemories(projectId: string, context: MemoryContext, limit?: number): Promise<Memory[]> {
|
|
740
|
+
return this._semanticMemories.getRelevantMemories(projectId, context, limit)
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
autoRemember(projectId: string, decisionType: string, value: string, context?: string): Promise<void> {
|
|
744
|
+
return this._semanticMemories.autoRemember(projectId, decisionType, value, context)
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
getAllMemories(projectId: string): Promise<Memory[]> {
|
|
748
|
+
return this._semanticMemories.getAllMemories(projectId)
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
getMemoryStats(projectId: string) {
|
|
752
|
+
return this._semanticMemories.getMemoryStats(projectId)
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
// ===========================================================================
|
|
756
|
+
// TIER 1: Session Memory
|
|
757
|
+
// ===========================================================================
|
|
758
|
+
|
|
759
|
+
setSession(key: string, value: unknown): void {
|
|
760
|
+
this._sessionStore.setSession(key, value)
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
getSession(key: string): unknown {
|
|
764
|
+
return this._sessionStore.getSession(key)
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
clearSession(): void {
|
|
768
|
+
this._sessionStore.clearSession()
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
// ===========================================================================
|
|
772
|
+
// TIER 2: Patterns
|
|
773
|
+
// ===========================================================================
|
|
774
|
+
|
|
775
|
+
loadPatterns(projectId: string) {
|
|
776
|
+
return this._patternStore.loadPatterns(projectId)
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
savePatterns(projectId: string) {
|
|
780
|
+
return this._patternStore.savePatterns(projectId)
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
recordDecision(projectId: string, key: string, value: string, context?: string): Promise<void> {
|
|
784
|
+
return this._patternStore.recordDecision(projectId, key, value, context)
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
getDecision(projectId: string, key: string): Promise<{ value: string; confidence: string } | null> {
|
|
788
|
+
return this._patternStore.getDecision(projectId, key)
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
hasPattern(projectId: string, key: string): Promise<boolean> {
|
|
792
|
+
return this._patternStore.hasPattern(projectId, key)
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
recordWorkflow(projectId: string, workflowName: string, pattern: Record<string, unknown>): Promise<void> {
|
|
796
|
+
return this._patternStore.recordWorkflow(projectId, workflowName, pattern)
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
getWorkflow(projectId: string, workflowName: string): Promise<Workflow | null> {
|
|
800
|
+
return this._patternStore.getWorkflow(projectId, workflowName)
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
setPreference(projectId: string, key: string, value: Preference['value']): Promise<void> {
|
|
804
|
+
return this._patternStore.setPreference(projectId, key, value)
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
getPreference(projectId: string, key: string, defaultValue?: unknown): Promise<unknown> {
|
|
808
|
+
return this._patternStore.getPreference(projectId, key, defaultValue)
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
getPatternsSummary(projectId: string) {
|
|
812
|
+
return this._patternStore.getPatternsSummary(projectId)
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// ===========================================================================
|
|
816
|
+
// TIER 3: History
|
|
817
|
+
// ===========================================================================
|
|
818
|
+
|
|
819
|
+
appendHistory(projectId: string, entry: Record<string, unknown> & { type: HistoryEventType }): Promise<void> {
|
|
820
|
+
return this._historyStore.appendHistory(projectId, entry)
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
getRecentHistory(projectId: string, limit?: number) {
|
|
824
|
+
return this._historyStore.getRecentHistory(projectId, limit)
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
// ===========================================================================
|
|
828
|
+
// CONVENIENCE: Combined operations
|
|
829
|
+
// ===========================================================================
|
|
830
|
+
|
|
831
|
+
async getSmartDecision(projectId: string, key: string): Promise<string | null> {
|
|
832
|
+
const sessionValue = this.getSession(`decision:${key}`)
|
|
833
|
+
if (sessionValue !== undefined) return sessionValue as string
|
|
834
|
+
|
|
835
|
+
const pattern = await this.getDecision(projectId, key)
|
|
836
|
+
if (pattern) return pattern.value
|
|
837
|
+
|
|
838
|
+
return null
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
async learnDecision(projectId: string, key: string, value: string, context: string = ''): Promise<void> {
|
|
842
|
+
this.setSession(`decision:${key}`, value)
|
|
843
|
+
await this.recordDecision(projectId, key, value, context)
|
|
844
|
+
await this.appendHistory(projectId, { type: 'decision', key, value, context })
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
/**
|
|
848
|
+
* Reset internal state (for testing)
|
|
849
|
+
*/
|
|
850
|
+
resetState(): void {
|
|
851
|
+
this._sessionStore.clearSession()
|
|
852
|
+
this._semanticMemories.reset()
|
|
853
|
+
this._patternStore.reset()
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
// =============================================================================
|
|
858
|
+
// Default Export
|
|
859
|
+
// =============================================================================
|
|
860
|
+
|
|
861
|
+
const memorySystem = new MemorySystem()
|
|
7
862
|
export default memorySystem
|
|
8
|
-
export { MemorySystem, MEMORY_TAGS } from './memory-system/index'
|