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