@try-works/dsh-memory 0.1.18 → 0.1.20

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 (51) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +585 -585
  3. package/STATUS.md +287 -287
  4. package/cordis.patch.yml +69 -69
  5. package/package.json +1 -1
  6. package/scripts/check-package.mjs +76 -76
  7. package/src/SYSTEM.template.md +311 -311
  8. package/src/capture.js +242 -242
  9. package/src/client/index.js +886 -886
  10. package/src/config.js +57 -57
  11. package/src/consolidate-run.js +132 -132
  12. package/src/consolidate.js +330 -330
  13. package/src/context.js +88 -88
  14. package/src/decisions.js +106 -106
  15. package/src/defaults.js +93 -93
  16. package/src/digest.js +255 -255
  17. package/src/entry.js +179 -179
  18. package/src/eval.js +72 -72
  19. package/src/files.js +60 -60
  20. package/src/fixture.js +56 -56
  21. package/src/forget.js +71 -71
  22. package/src/freshness.js +65 -65
  23. package/src/fts.js +166 -166
  24. package/src/git.js +53 -53
  25. package/src/history.js +226 -226
  26. package/src/host.js +866 -857
  27. package/src/index.js +115 -94
  28. package/src/inject.js +108 -108
  29. package/src/layers.js +178 -178
  30. package/src/ledger.js +19 -19
  31. package/src/map.js +184 -184
  32. package/src/models.js +136 -136
  33. package/src/queue.js +602 -602
  34. package/src/rank.js +31 -31
  35. package/src/recover.js +91 -91
  36. package/src/refresh.js +80 -80
  37. package/src/remember.js +58 -58
  38. package/src/rerank.js +90 -90
  39. package/src/search.js +75 -75
  40. package/src/select.js +57 -57
  41. package/src/session.js +60 -60
  42. package/src/state.js +240 -240
  43. package/src/store.js +128 -128
  44. package/src/summarize.js +195 -195
  45. package/src/system-doc.js +171 -171
  46. package/src/tasks.js +617 -617
  47. package/src/telemetry.js +122 -122
  48. package/src/tools.js +828 -818
  49. package/src/working.js +288 -288
  50. package/src/workspace-key.js +68 -68
  51. 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
+ }