@try-works/dsh-memory 0.1.19 → 0.1.21
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/LICENSE +21 -21
- package/README.md +585 -585
- package/STATUS.md +294 -287
- package/cordis.patch.yml +69 -69
- package/package.json +1 -1
- package/scripts/check-package.mjs +76 -76
- package/scripts/inspect-log.ts +40 -0
- package/scripts/repair-ignorable.ts +130 -0
- package/scripts/scan-sessions.ts +43 -0
- package/scripts/verify-log.ts +26 -0
- package/src/SYSTEM.template.md +311 -311
- package/src/capture.js +242 -242
- package/src/client/index.js +886 -886
- package/src/config.js +57 -57
- package/src/consolidate-run.js +132 -132
- package/src/consolidate.js +330 -330
- package/src/context.js +88 -88
- package/src/decisions.js +106 -106
- package/src/defaults.js +93 -93
- package/src/digest.js +255 -255
- package/src/entry.js +179 -179
- package/src/eval.js +72 -72
- package/src/files.js +60 -60
- package/src/fixture.js +56 -56
- package/src/forget.js +71 -71
- package/src/freshness.js +65 -65
- package/src/fts.js +166 -166
- package/src/git.js +53 -53
- package/src/history.js +226 -226
- package/src/host.js +884 -866
- package/src/index.js +115 -94
- package/src/inject.js +108 -108
- package/src/layers.js +178 -178
- package/src/ledger.js +19 -19
- package/src/map.js +184 -184
- package/src/models.js +136 -136
- package/src/queue.js +602 -602
- package/src/rank.js +31 -31
- package/src/recover.js +91 -91
- package/src/refresh.js +80 -80
- package/src/remember.js +58 -58
- package/src/rerank.js +90 -90
- package/src/search.js +75 -75
- package/src/select.js +57 -57
- package/src/session.js +60 -60
- package/src/state.js +240 -240
- package/src/store.js +128 -128
- package/src/summarize.js +195 -195
- package/src/system-doc.js +171 -171
- package/src/tasks.js +617 -617
- package/src/telemetry.js +122 -122
- package/src/tools.js +828 -818
- package/src/working.js +288 -288
- package/src/workspace-key.js +68 -68
- package/src/workspace.js +87 -87
package/src/store.js
CHANGED
|
@@ -1,128 +1,128 @@
|
|
|
1
|
-
// Memory store layout: paths, .gitignore, memory.json, layer templates, and the
|
|
2
|
-
// MemoryStore class. Node built-ins only (unit-testable standalone).
|
|
3
|
-
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
4
|
-
import { join } from 'node:path'
|
|
5
|
-
import { resolveConfig } from './defaults.js'
|
|
6
|
-
import { GLOBAL_LAYERED, LAYER_DIRS, LAYERED } from './entry.js'
|
|
7
|
-
import { sanitizeKey, deriveWorkspaceKey } from './workspace-key.js'
|
|
8
|
-
import { workingPreamble } from './working.js'
|
|
9
|
-
|
|
10
|
-
export const LAYER_FILES = ['INDEX.md', 'semantic.md', 'episodic.md', 'procedural.md', 'working.md', 'learnings.md', 'junk.md', 'codebase.md', 'history.md', 'decisions.md', 'state.md']
|
|
11
|
-
|
|
12
|
-
/** Working memory is the current-state head of ONE repo/workspace, never of the
|
|
13
|
-
* whole store: a global working.md would mix unrelated repositories together. */
|
|
14
|
-
export const WORKSPACE_LAYER_FILES = LAYER_FILES
|
|
15
|
-
/** working.md is per workspace; the codebase map describes one repository. */
|
|
16
|
-
export const WORKSPACE_ONLY_FILES = ['working.md', 'codebase.md', 'history.md', 'state.md']
|
|
17
|
-
export const GLOBAL_LAYER_FILES = LAYER_FILES.filter((file) => !WORKSPACE_ONLY_FILES.includes(file))
|
|
18
|
-
|
|
19
|
-
export const GITIGNORE = [
|
|
20
|
-
'# dsh-memory: derived + device-local state (regenerated; never committed)',
|
|
21
|
-
'index/', 'state/', 'digests/', 'logs/', '*.lock', '.dsh-tmp/', '',
|
|
22
|
-
].join('\n')
|
|
23
|
-
|
|
24
|
-
export function globalDir(root) { return join(root, 'global') }
|
|
25
|
-
export function workspacesDir(root) { return join(root, 'workspaces') }
|
|
26
|
-
export function workspaceDir(root, key) { return join(workspacesDir(root), key) }
|
|
27
|
-
export function layerDir(root, scope, key) { return scope === 'global' ? globalDir(root) : workspaceDir(root, key) }
|
|
28
|
-
export function memoryJsonPath(root) { return join(root, 'memory.json') }
|
|
29
|
-
|
|
30
|
-
export function memoryJsonTemplate() { return { schemaVersion: 1, git: { remote: '', branch: 'main' }, workspaces: {} } }
|
|
31
|
-
|
|
32
|
-
export function layerTemplate(scope, key, file) {
|
|
33
|
-
const title = scope === 'global' ? 'Global' : (key ?? 'workspace')
|
|
34
|
-
if (file === 'INDEX.md') return '# ' + title + ' memory — index\n\nTopic map for the ' + scope + ' layer. One line per topic, linking into a layer file section.\n\n'
|
|
35
|
-
if (file === 'working.md') return '# ' + title + ' — working memory' + String.fromCharCode(10,10) + workingPreamble() + String.fromCharCode(10,10)
|
|
36
|
-
if (file === 'codebase.md') return '# ' + title + ' — codebase\n\nMap of this repository: one entry per module or subsystem as it is discovered.\n\n'
|
|
37
|
-
if (file === 'history.md') return '# ' + title + ' — history\n\nHow this repository changed: one entry per commit, grouped by day.\n\n'
|
|
38
|
-
if (file === 'state.md') return '# ' + title + ' — state\n\nWhat this repository is right now, section by section. Each section carries when it was last verified.\n\n'
|
|
39
|
-
if (file === 'decisions.md') return '# ' + title + ' — decisions\n\nSettled choices and why they won: dependencies, conventions, architecture. One entry per decision.\n\n'
|
|
40
|
-
return '# ' + title + ' — ' + file.replace(/\.md$/, '') + '\n\n'
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/** Last recorded sync outcome (state/sync.json) plus whether a conflict marker stands. */
|
|
44
|
-
function readSyncState(root) {
|
|
45
|
-
const state = { last: null, conflicted: existsSync(join(root, 'state', 'CONFLICT.md')) }
|
|
46
|
-
try { state.last = JSON.parse(readFileSync(join(root, 'state', 'sync.json'), 'utf8')) } catch {}
|
|
47
|
-
return state
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function writeIfAbsent(file, content) { if (!existsSync(file)) writeFileSync(file, content, 'utf8') }
|
|
51
|
-
|
|
52
|
-
/** Drop a pre-existing global working.md, but only while it is still the untouched
|
|
53
|
-
* template - anything the user actually wrote there is theirs to keep. */
|
|
54
|
-
function retireGlobalWorking(root) {
|
|
55
|
-
const file = join(globalDir(root), 'working.md')
|
|
56
|
-
try {
|
|
57
|
-
if (!existsSync(file)) return
|
|
58
|
-
if (readFileSync(file, 'utf8') !== layerTemplate('global', '', 'working.md')) return
|
|
59
|
-
rmSync(file)
|
|
60
|
-
} catch {}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export class MemoryStore {
|
|
64
|
-
constructor(config = {}) {
|
|
65
|
-
this.config = resolveConfig(config)
|
|
66
|
-
this.root = this.config.memoryRoot
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
get globalPath() { return globalDir(this.root) }
|
|
70
|
-
get workspacesPath() { return workspacesDir(this.root) }
|
|
71
|
-
|
|
72
|
-
resolveKey({ explicit, remote, basename } = {}) {
|
|
73
|
-
return sanitizeKey(explicit) ?? deriveWorkspaceKey({ remote, basename })
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
ensureLayout() {
|
|
77
|
-
for (const dir of [this.root, this.globalPath, this.workspacesPath, join(this.root, 'index'), join(this.root, 'state'), join(this.root, 'digests'), join(this.root, 'logs')]) {
|
|
78
|
-
mkdirSync(dir, { recursive: true })
|
|
79
|
-
}
|
|
80
|
-
writeIfAbsent(join(this.root, '.gitignore'), GITIGNORE)
|
|
81
|
-
writeIfAbsent(memoryJsonPath(this.root), JSON.stringify(memoryJsonTemplate(), null, 2) + '\n')
|
|
82
|
-
for (const file of GLOBAL_LAYER_FILES) writeIfAbsent(join(this.globalPath, file), layerTemplate('global', '', file))
|
|
83
|
-
for (const dir of GLOBAL_LAYERED) mkdirSync(join(this.globalPath, LAYER_DIRS[dir]), { recursive: true })
|
|
84
|
-
retireGlobalWorking(this.root)
|
|
85
|
-
return this.root
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
ensureWorkspace(key, meta = {}) {
|
|
89
|
-
const safe = sanitizeKey(key)
|
|
90
|
-
if (!safe) throw new Error('invalid workspace key: ' + String(key))
|
|
91
|
-
const dir = workspaceDir(this.root, safe)
|
|
92
|
-
mkdirSync(dir, { recursive: true })
|
|
93
|
-
writeIfAbsent(join(dir, 'meta.json'), JSON.stringify({ key: safe, title: meta.title ?? safe, remotes: meta.remotes ?? [], createdAt: new Date().toISOString() }, null, 2) + '\n')
|
|
94
|
-
for (const file of WORKSPACE_LAYER_FILES) writeIfAbsent(join(dir, file), layerTemplate('workspace', safe, file))
|
|
95
|
-
for (const name of LAYERED) mkdirSync(join(dir, LAYER_DIRS[name]), { recursive: true })
|
|
96
|
-
return safe
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
readMemoryJson() { try { return JSON.parse(readFileSync(memoryJsonPath(this.root), 'utf8')) } catch { return memoryJsonTemplate() } }
|
|
100
|
-
writeMemoryJson(value) { writeFileSync(memoryJsonPath(this.root), JSON.stringify(value, null, 2) + '\n') }
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Keep the registry's git block in step with the resolved configuration. memory.json is
|
|
104
|
-
* committed, so it should say where this store syncs; it previously stayed empty because
|
|
105
|
-
* only workspace association ever wrote the file.
|
|
106
|
-
*/
|
|
107
|
-
syncGitBlock() {
|
|
108
|
-
const mem = this.readMemoryJson()
|
|
109
|
-
const remote = this.config.gitRemote ?? ''
|
|
110
|
-
const branch = this.config.gitBranch ?? 'main'
|
|
111
|
-
if ((mem.git?.remote ?? '') === remote && (mem.git?.branch ?? '') === branch) return false
|
|
112
|
-
mem.git = { remote, branch }
|
|
113
|
-
this.writeMemoryJson(mem)
|
|
114
|
-
return true
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
status() {
|
|
118
|
-
return {
|
|
119
|
-
root: this.root,
|
|
120
|
-
initialized: existsSync(memoryJsonPath(this.root)),
|
|
121
|
-
gitIgnored: existsSync(join(this.root, '.gitignore')),
|
|
122
|
-
globalLayer: existsSync(this.globalPath),
|
|
123
|
-
sync: readSyncState(this.root),
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
dispose() {}
|
|
128
|
-
}
|
|
1
|
+
// Memory store layout: paths, .gitignore, memory.json, layer templates, and the
|
|
2
|
+
// MemoryStore class. Node built-ins only (unit-testable standalone).
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
4
|
+
import { join } from 'node:path'
|
|
5
|
+
import { resolveConfig } from './defaults.js'
|
|
6
|
+
import { GLOBAL_LAYERED, LAYER_DIRS, LAYERED } from './entry.js'
|
|
7
|
+
import { sanitizeKey, deriveWorkspaceKey } from './workspace-key.js'
|
|
8
|
+
import { workingPreamble } from './working.js'
|
|
9
|
+
|
|
10
|
+
export const LAYER_FILES = ['INDEX.md', 'semantic.md', 'episodic.md', 'procedural.md', 'working.md', 'learnings.md', 'junk.md', 'codebase.md', 'history.md', 'decisions.md', 'state.md']
|
|
11
|
+
|
|
12
|
+
/** Working memory is the current-state head of ONE repo/workspace, never of the
|
|
13
|
+
* whole store: a global working.md would mix unrelated repositories together. */
|
|
14
|
+
export const WORKSPACE_LAYER_FILES = LAYER_FILES
|
|
15
|
+
/** working.md is per workspace; the codebase map describes one repository. */
|
|
16
|
+
export const WORKSPACE_ONLY_FILES = ['working.md', 'codebase.md', 'history.md', 'state.md']
|
|
17
|
+
export const GLOBAL_LAYER_FILES = LAYER_FILES.filter((file) => !WORKSPACE_ONLY_FILES.includes(file))
|
|
18
|
+
|
|
19
|
+
export const GITIGNORE = [
|
|
20
|
+
'# dsh-memory: derived + device-local state (regenerated; never committed)',
|
|
21
|
+
'index/', 'state/', 'digests/', 'logs/', '*.lock', '.dsh-tmp/', '',
|
|
22
|
+
].join('\n')
|
|
23
|
+
|
|
24
|
+
export function globalDir(root) { return join(root, 'global') }
|
|
25
|
+
export function workspacesDir(root) { return join(root, 'workspaces') }
|
|
26
|
+
export function workspaceDir(root, key) { return join(workspacesDir(root), key) }
|
|
27
|
+
export function layerDir(root, scope, key) { return scope === 'global' ? globalDir(root) : workspaceDir(root, key) }
|
|
28
|
+
export function memoryJsonPath(root) { return join(root, 'memory.json') }
|
|
29
|
+
|
|
30
|
+
export function memoryJsonTemplate() { return { schemaVersion: 1, git: { remote: '', branch: 'main' }, workspaces: {} } }
|
|
31
|
+
|
|
32
|
+
export function layerTemplate(scope, key, file) {
|
|
33
|
+
const title = scope === 'global' ? 'Global' : (key ?? 'workspace')
|
|
34
|
+
if (file === 'INDEX.md') return '# ' + title + ' memory — index\n\nTopic map for the ' + scope + ' layer. One line per topic, linking into a layer file section.\n\n'
|
|
35
|
+
if (file === 'working.md') return '# ' + title + ' — working memory' + String.fromCharCode(10,10) + workingPreamble() + String.fromCharCode(10,10)
|
|
36
|
+
if (file === 'codebase.md') return '# ' + title + ' — codebase\n\nMap of this repository: one entry per module or subsystem as it is discovered.\n\n'
|
|
37
|
+
if (file === 'history.md') return '# ' + title + ' — history\n\nHow this repository changed: one entry per commit, grouped by day.\n\n'
|
|
38
|
+
if (file === 'state.md') return '# ' + title + ' — state\n\nWhat this repository is right now, section by section. Each section carries when it was last verified.\n\n'
|
|
39
|
+
if (file === 'decisions.md') return '# ' + title + ' — decisions\n\nSettled choices and why they won: dependencies, conventions, architecture. One entry per decision.\n\n'
|
|
40
|
+
return '# ' + title + ' — ' + file.replace(/\.md$/, '') + '\n\n'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Last recorded sync outcome (state/sync.json) plus whether a conflict marker stands. */
|
|
44
|
+
function readSyncState(root) {
|
|
45
|
+
const state = { last: null, conflicted: existsSync(join(root, 'state', 'CONFLICT.md')) }
|
|
46
|
+
try { state.last = JSON.parse(readFileSync(join(root, 'state', 'sync.json'), 'utf8')) } catch {}
|
|
47
|
+
return state
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function writeIfAbsent(file, content) { if (!existsSync(file)) writeFileSync(file, content, 'utf8') }
|
|
51
|
+
|
|
52
|
+
/** Drop a pre-existing global working.md, but only while it is still the untouched
|
|
53
|
+
* template - anything the user actually wrote there is theirs to keep. */
|
|
54
|
+
function retireGlobalWorking(root) {
|
|
55
|
+
const file = join(globalDir(root), 'working.md')
|
|
56
|
+
try {
|
|
57
|
+
if (!existsSync(file)) return
|
|
58
|
+
if (readFileSync(file, 'utf8') !== layerTemplate('global', '', 'working.md')) return
|
|
59
|
+
rmSync(file)
|
|
60
|
+
} catch {}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export class MemoryStore {
|
|
64
|
+
constructor(config = {}) {
|
|
65
|
+
this.config = resolveConfig(config)
|
|
66
|
+
this.root = this.config.memoryRoot
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
get globalPath() { return globalDir(this.root) }
|
|
70
|
+
get workspacesPath() { return workspacesDir(this.root) }
|
|
71
|
+
|
|
72
|
+
resolveKey({ explicit, remote, basename } = {}) {
|
|
73
|
+
return sanitizeKey(explicit) ?? deriveWorkspaceKey({ remote, basename })
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
ensureLayout() {
|
|
77
|
+
for (const dir of [this.root, this.globalPath, this.workspacesPath, join(this.root, 'index'), join(this.root, 'state'), join(this.root, 'digests'), join(this.root, 'logs')]) {
|
|
78
|
+
mkdirSync(dir, { recursive: true })
|
|
79
|
+
}
|
|
80
|
+
writeIfAbsent(join(this.root, '.gitignore'), GITIGNORE)
|
|
81
|
+
writeIfAbsent(memoryJsonPath(this.root), JSON.stringify(memoryJsonTemplate(), null, 2) + '\n')
|
|
82
|
+
for (const file of GLOBAL_LAYER_FILES) writeIfAbsent(join(this.globalPath, file), layerTemplate('global', '', file))
|
|
83
|
+
for (const dir of GLOBAL_LAYERED) mkdirSync(join(this.globalPath, LAYER_DIRS[dir]), { recursive: true })
|
|
84
|
+
retireGlobalWorking(this.root)
|
|
85
|
+
return this.root
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
ensureWorkspace(key, meta = {}) {
|
|
89
|
+
const safe = sanitizeKey(key)
|
|
90
|
+
if (!safe) throw new Error('invalid workspace key: ' + String(key))
|
|
91
|
+
const dir = workspaceDir(this.root, safe)
|
|
92
|
+
mkdirSync(dir, { recursive: true })
|
|
93
|
+
writeIfAbsent(join(dir, 'meta.json'), JSON.stringify({ key: safe, title: meta.title ?? safe, remotes: meta.remotes ?? [], createdAt: new Date().toISOString() }, null, 2) + '\n')
|
|
94
|
+
for (const file of WORKSPACE_LAYER_FILES) writeIfAbsent(join(dir, file), layerTemplate('workspace', safe, file))
|
|
95
|
+
for (const name of LAYERED) mkdirSync(join(dir, LAYER_DIRS[name]), { recursive: true })
|
|
96
|
+
return safe
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
readMemoryJson() { try { return JSON.parse(readFileSync(memoryJsonPath(this.root), 'utf8')) } catch { return memoryJsonTemplate() } }
|
|
100
|
+
writeMemoryJson(value) { writeFileSync(memoryJsonPath(this.root), JSON.stringify(value, null, 2) + '\n') }
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Keep the registry's git block in step with the resolved configuration. memory.json is
|
|
104
|
+
* committed, so it should say where this store syncs; it previously stayed empty because
|
|
105
|
+
* only workspace association ever wrote the file.
|
|
106
|
+
*/
|
|
107
|
+
syncGitBlock() {
|
|
108
|
+
const mem = this.readMemoryJson()
|
|
109
|
+
const remote = this.config.gitRemote ?? ''
|
|
110
|
+
const branch = this.config.gitBranch ?? 'main'
|
|
111
|
+
if ((mem.git?.remote ?? '') === remote && (mem.git?.branch ?? '') === branch) return false
|
|
112
|
+
mem.git = { remote, branch }
|
|
113
|
+
this.writeMemoryJson(mem)
|
|
114
|
+
return true
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
status() {
|
|
118
|
+
return {
|
|
119
|
+
root: this.root,
|
|
120
|
+
initialized: existsSync(memoryJsonPath(this.root)),
|
|
121
|
+
gitIgnored: existsSync(join(this.root, '.gitignore')),
|
|
122
|
+
globalLayer: existsSync(this.globalPath),
|
|
123
|
+
sync: readSyncState(this.root),
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
dispose() {}
|
|
128
|
+
}
|