lsd-pi 1.1.2 → 1.1.3

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 (183) hide show
  1. package/README.md +2 -1
  2. package/dist/bedrock-auth.d.ts +25 -0
  3. package/dist/bedrock-auth.js +59 -0
  4. package/dist/headless.js +8 -3
  5. package/dist/loader.js +1 -0
  6. package/dist/onboarding-llm.d.ts +37 -0
  7. package/dist/onboarding-llm.js +64 -0
  8. package/dist/onboarding.d.ts +2 -14
  9. package/dist/onboarding.js +146 -71
  10. package/dist/pi-migration.js +1 -0
  11. package/dist/resources/extensions/memory/auto-extract.js +21 -3
  12. package/dist/resources/extensions/memory/dream.js +703 -0
  13. package/dist/resources/extensions/memory/extension-manifest.json +2 -2
  14. package/dist/resources/extensions/memory/index.js +115 -8
  15. package/dist/resources/extensions/slash-commands/extension-manifest.json +10 -10
  16. package/dist/resources/extensions/slash-commands/index.js +0 -4
  17. package/dist/resources/extensions/slash-commands/plan.js +181 -45
  18. package/dist/resources/extensions/subagent/agents.js +14 -1
  19. package/dist/resources/extensions/subagent/configured-model.js +3 -2
  20. package/dist/resources/extensions/subagent/index.js +34 -28
  21. package/dist/resources/extensions/subagent/launch-helpers.js +24 -0
  22. package/dist/resources/extensions/subagent/model-resolution.js +41 -3
  23. package/dist/resources/extensions/usage/extension-manifest.json +11 -0
  24. package/dist/resources/extensions/usage/index.js +346 -0
  25. package/{src/resources/skills/create-gsd-extension → dist/resources/skills/create-lsd-extension}/SKILL.md +6 -6
  26. package/{src/resources/skills/create-gsd-extension → dist/resources/skills/create-lsd-extension}/references/custom-tools.md +1 -1
  27. package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/extension-lifecycle.md +2 -2
  28. package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/extensioncontext-reference.md +1 -1
  29. package/{src/resources/skills/create-gsd-extension → dist/resources/skills/create-lsd-extension}/references/key-rules-gotchas.md +4 -4
  30. package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/packaging-distribution.md +6 -6
  31. package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/workflows/create-extension.md +3 -3
  32. package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/workflows/debug-extension.md +5 -5
  33. package/dist/resources/skills/teams-debug/SKILL.md +5 -6
  34. package/dist/resources/skills/teams-document/SKILL.md +1 -2
  35. package/dist/resources/skills/teams-plan/SKILL.md +3 -4
  36. package/dist/resources/skills/teams-run/SKILL.md +3 -4
  37. package/dist/resources/skills/teams-verify/SKILL.md +4 -5
  38. package/dist/startup-model-validation.js +1 -0
  39. package/dist/welcome-screen.js +13 -11
  40. package/dist/wizard.js +12 -0
  41. package/package.json +1 -1
  42. package/packages/pi-ai/dist/models.generated.d.ts +688 -409
  43. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  44. package/packages/pi-ai/dist/models.generated.js +761 -488
  45. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  46. package/packages/pi-ai/scripts/generate-models.ts +40 -18
  47. package/packages/pi-ai/src/models.generated.ts +759 -486
  48. package/packages/pi-coding-agent/dist/cli/config-selector.js +1 -1
  49. package/packages/pi-coding-agent/dist/cli/config-selector.js.map +1 -1
  50. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +1 -2
  51. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  52. package/packages/pi-coding-agent/dist/core/agent-session.js +6 -30
  53. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  54. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +6 -0
  55. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  56. package/packages/pi-coding-agent/dist/core/settings-manager.js +44 -1
  57. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  58. package/packages/pi-coding-agent/dist/core/skill-tool.test.js +9 -5
  59. package/packages/pi-coding-agent/dist/core/skill-tool.test.js.map +1 -1
  60. package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
  61. package/packages/pi-coding-agent/dist/core/skills.js +3 -2
  62. package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
  63. package/packages/pi-coding-agent/dist/index.d.ts +1 -1
  64. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  65. package/packages/pi-coding-agent/dist/index.js +1 -1
  66. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  67. package/packages/pi-coding-agent/dist/main.js +1 -1
  68. package/packages/pi-coding-agent/dist/main.js.map +1 -1
  69. package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js +2 -2
  70. package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js.map +1 -1
  71. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  72. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +15 -12
  73. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  74. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +6 -0
  75. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  76. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +25 -1
  77. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  78. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +1 -1
  79. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  80. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  81. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +10 -0
  82. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  83. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js +1 -1
  84. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js.map +1 -1
  85. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  86. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +31 -22
  87. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  88. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +18 -5
  89. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  90. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +139 -20
  91. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
  92. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  93. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +4 -0
  94. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
  95. package/packages/pi-coding-agent/package.json +1 -1
  96. package/packages/pi-coding-agent/src/cli/config-selector.ts +1 -1
  97. package/packages/pi-coding-agent/src/core/agent-session.ts +5 -28
  98. package/packages/pi-coding-agent/src/core/settings-manager.ts +52 -1
  99. package/packages/pi-coding-agent/src/core/skill-tool.test.ts +18 -5
  100. package/packages/pi-coding-agent/src/core/skills.ts +3 -2
  101. package/packages/pi-coding-agent/src/index.ts +1 -1
  102. package/packages/pi-coding-agent/src/main.ts +1 -1
  103. package/packages/pi-coding-agent/src/modes/interactive/components/bash-execution.ts +2 -2
  104. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +12 -13
  105. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +39 -1
  106. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +1 -1
  107. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +10 -0
  108. package/packages/pi-coding-agent/src/modes/interactive/controllers/extension-ui-controller.ts +1 -1
  109. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +46 -20
  110. package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +171 -20
  111. package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +4 -0
  112. package/packages/pi-tui/dist/components/editor.d.ts +1 -0
  113. package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
  114. package/packages/pi-tui/dist/components/editor.js +23 -0
  115. package/packages/pi-tui/dist/components/editor.js.map +1 -1
  116. package/packages/pi-tui/src/components/editor.ts +23 -0
  117. package/pkg/dist/modes/interactive/theme/theme.d.ts +18 -5
  118. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  119. package/pkg/dist/modes/interactive/theme/theme.js +139 -20
  120. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
  121. package/pkg/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  122. package/pkg/dist/modes/interactive/theme/themes.js +4 -0
  123. package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
  124. package/pkg/package.json +1 -1
  125. package/src/resources/extensions/memory/auto-extract.ts +23 -3
  126. package/src/resources/extensions/memory/dream.ts +814 -0
  127. package/src/resources/extensions/memory/extension-manifest.json +2 -2
  128. package/src/resources/extensions/memory/index.ts +134 -13
  129. package/src/resources/extensions/memory/tests/auto-extract.test.ts +10 -2
  130. package/src/resources/extensions/memory/tests/dream.test.ts +142 -0
  131. package/src/resources/extensions/slash-commands/extension-manifest.json +10 -10
  132. package/src/resources/extensions/slash-commands/index.ts +3 -7
  133. package/src/resources/extensions/slash-commands/plan.ts +192 -46
  134. package/src/resources/extensions/subagent/agents.ts +11 -1
  135. package/src/resources/extensions/subagent/configured-model.ts +3 -2
  136. package/src/resources/extensions/subagent/index.ts +38 -30
  137. package/src/resources/extensions/subagent/launch-helpers.ts +30 -0
  138. package/src/resources/extensions/subagent/model-resolution.ts +40 -3
  139. package/src/resources/extensions/usage/extension-manifest.json +11 -0
  140. package/src/resources/extensions/usage/index.ts +441 -0
  141. package/{dist/resources/skills/create-gsd-extension → src/resources/skills/create-lsd-extension}/SKILL.md +6 -6
  142. package/{dist/resources/skills/create-gsd-extension → src/resources/skills/create-lsd-extension}/references/custom-tools.md +1 -1
  143. package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/extension-lifecycle.md +2 -2
  144. package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/extensioncontext-reference.md +1 -1
  145. package/{dist/resources/skills/create-gsd-extension → src/resources/skills/create-lsd-extension}/references/key-rules-gotchas.md +4 -4
  146. package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/packaging-distribution.md +6 -6
  147. package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/workflows/create-extension.md +3 -3
  148. package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/workflows/debug-extension.md +5 -5
  149. package/src/resources/skills/teams-debug/SKILL.md +5 -6
  150. package/src/resources/skills/teams-document/SKILL.md +1 -2
  151. package/src/resources/skills/teams-plan/SKILL.md +3 -4
  152. package/src/resources/skills/teams-run/SKILL.md +3 -4
  153. package/src/resources/skills/teams-verify/SKILL.md +4 -5
  154. package/dist/resources/extensions/slash-commands/create-extension.js +0 -264
  155. package/dist/resources/extensions/slash-commands/create-slash-command.js +0 -208
  156. package/src/resources/extensions/slash-commands/create-extension.ts +0 -297
  157. package/src/resources/extensions/slash-commands/create-slash-command.ts +0 -234
  158. /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/compaction-session-control.md +0 -0
  159. /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/custom-commands.md +0 -0
  160. /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/custom-rendering.md +0 -0
  161. /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/custom-ui.md +0 -0
  162. /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/events-reference.md +0 -0
  163. /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/extensionapi-reference.md +0 -0
  164. /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/mode-behavior.md +0 -0
  165. /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/model-provider-management.md +0 -0
  166. /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/remote-execution-overrides.md +0 -0
  167. /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/state-management.md +0 -0
  168. /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/system-prompt-modification.md +0 -0
  169. /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/workflows/add-capability.md +0 -0
  170. /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/compaction-session-control.md +0 -0
  171. /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/custom-commands.md +0 -0
  172. /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/custom-rendering.md +0 -0
  173. /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/custom-ui.md +0 -0
  174. /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/events-reference.md +0 -0
  175. /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/extensionapi-reference.md +0 -0
  176. /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/mode-behavior.md +0 -0
  177. /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/model-provider-management.md +0 -0
  178. /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/remote-execution-overrides.md +0 -0
  179. /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/state-management.md +0 -0
  180. /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/system-prompt-modification.md +0 -0
  181. /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/templates/extension-skeleton.ts +0 -0
  182. /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/templates/stateful-tool-skeleton.ts +0 -0
  183. /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/workflows/add-capability.md +0 -0
@@ -0,0 +1,703 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { spawn } from 'node:child_process';
3
+ import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, unlinkSync, writeFileSync, } from 'node:fs';
4
+ import { tmpdir } from 'node:os';
5
+ import { dirname, isAbsolute, join, resolve } from 'node:path';
6
+ import { CONFIG_DIR_NAME, getAgentDir } from '@gsd/pi-coding-agent';
7
+ import { getMemoryDir } from './memory-paths.js';
8
+ import { readBudgetMemoryModel, resolveCliPath } from './auto-extract.js';
9
+ const DEFAULT_AUTO_DREAM_SETTINGS = {
10
+ enabled: false,
11
+ minHours: 24,
12
+ minSessions: 5,
13
+ };
14
+ const LOCK_FILE = '.consolidate-lock';
15
+ const AUDIT_FILE = '.last-dream.txt';
16
+ const LOG_FILE = '.last-dream.log';
17
+ const HOLDER_STALE_MS = 60 * 60 * 1000;
18
+ const SESSION_SCAN_INTERVAL_MS = 10 * 60 * 1000;
19
+ const READ_ONLY_BASH_RE = /^\s*(cat|head|tail|less|more|wc|file|stat|du|df|which|type|echo|printf|ls|find|grep|rg|awk|sed\b(?!.*-i)|sort|uniq|diff|comm|tr|cut)\b/;
20
+ let lastAutoDreamScanAt = 0;
21
+ function getProjectSettingsPath(cwd) {
22
+ return join(cwd, CONFIG_DIR_NAME, 'settings.json');
23
+ }
24
+ function readJsonFile(path) {
25
+ try {
26
+ if (!existsSync(path))
27
+ return {};
28
+ const raw = readFileSync(path, 'utf-8');
29
+ const parsed = JSON.parse(raw);
30
+ return parsed && typeof parsed === 'object' ? parsed : {};
31
+ }
32
+ catch {
33
+ return {};
34
+ }
35
+ }
36
+ function parseAutoDreamSettings(source) {
37
+ const memory = source.memory;
38
+ if (!memory || typeof memory !== 'object')
39
+ return {};
40
+ const settings = memory;
41
+ return {
42
+ enabled: typeof settings.autoDream === 'boolean'
43
+ ? settings.autoDream
44
+ : DEFAULT_AUTO_DREAM_SETTINGS.enabled,
45
+ minHours: typeof settings.autoDreamMinHours === 'number' && Number.isFinite(settings.autoDreamMinHours)
46
+ ? Math.max(1, settings.autoDreamMinHours)
47
+ : DEFAULT_AUTO_DREAM_SETTINGS.minHours,
48
+ minSessions: typeof settings.autoDreamMinSessions === 'number' && Number.isFinite(settings.autoDreamMinSessions)
49
+ ? Math.max(1, Math.floor(settings.autoDreamMinSessions))
50
+ : DEFAULT_AUTO_DREAM_SETTINGS.minSessions,
51
+ };
52
+ }
53
+ export function readAutoDreamSettings(cwd) {
54
+ const global = parseAutoDreamSettings(readJsonFile(join(getAgentDir(), 'settings.json')));
55
+ const project = parseAutoDreamSettings(readJsonFile(getProjectSettingsPath(cwd)));
56
+ return {
57
+ ...DEFAULT_AUTO_DREAM_SETTINGS,
58
+ ...global,
59
+ ...project,
60
+ };
61
+ }
62
+ export function setProjectAutoDreamEnabled(cwd, enabled) {
63
+ const settingsPath = getProjectSettingsPath(cwd);
64
+ const next = readJsonFile(settingsPath);
65
+ const memory = next.memory && typeof next.memory === 'object'
66
+ ? { ...next.memory }
67
+ : {};
68
+ memory.autoDream = enabled;
69
+ next.memory = memory;
70
+ mkdirSync(dirname(settingsPath), { recursive: true });
71
+ writeFileSync(settingsPath, JSON.stringify(next, null, 2) + '\n', 'utf-8');
72
+ return readAutoDreamSettings(cwd);
73
+ }
74
+ function getLockPath(memoryDir) {
75
+ return join(memoryDir, LOCK_FILE);
76
+ }
77
+ function getAuditPath(memoryDir) {
78
+ return join(memoryDir, AUDIT_FILE);
79
+ }
80
+ function getLogPath(memoryDir) {
81
+ return join(memoryDir, LOG_FILE);
82
+ }
83
+ export function readLastConsolidatedAt(memoryDir) {
84
+ try {
85
+ return statSync(getLockPath(memoryDir)).mtimeMs;
86
+ }
87
+ catch {
88
+ return 0;
89
+ }
90
+ }
91
+ function isConsolidationInProgress(memoryDir) {
92
+ const lockPath = getLockPath(memoryDir);
93
+ try {
94
+ const stat = statSync(lockPath);
95
+ if (Date.now() - stat.mtimeMs > HOLDER_STALE_MS)
96
+ return false;
97
+ const pid = Number.parseInt(readFileSync(lockPath, 'utf-8').trim(), 10);
98
+ if (!Number.isFinite(pid) || pid <= 0)
99
+ return false;
100
+ process.kill(pid, 0);
101
+ return true;
102
+ }
103
+ catch {
104
+ return false;
105
+ }
106
+ }
107
+ export function tryAcquireConsolidationLock(memoryDir) {
108
+ mkdirSync(memoryDir, { recursive: true });
109
+ const lockPath = getLockPath(memoryDir);
110
+ let priorMtime = 0;
111
+ try {
112
+ priorMtime = statSync(lockPath).mtimeMs;
113
+ }
114
+ catch {
115
+ priorMtime = 0;
116
+ }
117
+ if (isConsolidationInProgress(memoryDir)) {
118
+ return null;
119
+ }
120
+ writeFileSync(lockPath, String(process.pid), 'utf-8');
121
+ return priorMtime;
122
+ }
123
+ function parseAuditFile(path) {
124
+ try {
125
+ const raw = readFileSync(path, 'utf-8');
126
+ const result = {};
127
+ for (const line of raw.split(/\r?\n/)) {
128
+ const idx = line.indexOf(':');
129
+ if (idx === -1)
130
+ continue;
131
+ const key = line.slice(0, idx).trim();
132
+ const value = line.slice(idx + 1).trim();
133
+ if (key)
134
+ result[key] = value;
135
+ }
136
+ return result;
137
+ }
138
+ catch {
139
+ return {};
140
+ }
141
+ }
142
+ function listSessionsTouchedSince(sessionDir, sinceMs, currentSessionFile) {
143
+ try {
144
+ const files = readdirSync(sessionDir)
145
+ .filter((name) => name.endsWith('.jsonl'))
146
+ .map((name) => join(sessionDir, name));
147
+ return files.filter((file) => {
148
+ if (currentSessionFile && resolve(file) === resolve(currentSessionFile))
149
+ return false;
150
+ try {
151
+ return statSync(file).mtimeMs > sinceMs;
152
+ }
153
+ catch {
154
+ return false;
155
+ }
156
+ });
157
+ }
158
+ catch {
159
+ return [];
160
+ }
161
+ }
162
+ function isPathInsideDir(targetPath, dir) {
163
+ const resolvedDir = resolve(dir);
164
+ const resolvedTarget = resolve(targetPath);
165
+ return resolvedTarget === resolvedDir || resolvedTarget.startsWith(resolvedDir + '/');
166
+ }
167
+ export function listBrokenMemoryIndexEntries(memoryDir) {
168
+ const entrypoint = join(memoryDir, 'MEMORY.md');
169
+ try {
170
+ const raw = readFileSync(entrypoint, 'utf-8');
171
+ const broken = new Set();
172
+ const linkRe = /\[[^\]]+\]\(([^)]+)\)/g;
173
+ for (const match of raw.matchAll(linkRe)) {
174
+ const href = match[1]?.trim();
175
+ if (!href || href.startsWith('#') || /^[a-z][a-z0-9+.-]*:/i.test(href))
176
+ continue;
177
+ const target = isAbsolute(href) ? resolve(href) : resolve(memoryDir, href);
178
+ if (!isPathInsideDir(target, memoryDir)) {
179
+ broken.add(href);
180
+ continue;
181
+ }
182
+ if (!existsSync(target)) {
183
+ broken.add(href);
184
+ }
185
+ }
186
+ return [...broken].sort();
187
+ }
188
+ catch {
189
+ return [];
190
+ }
191
+ }
192
+ export function pruneBrokenMemoryIndexEntries(memoryDir) {
193
+ const entrypoint = join(memoryDir, 'MEMORY.md');
194
+ try {
195
+ const raw = readFileSync(entrypoint, 'utf-8');
196
+ const broken = new Set(listBrokenMemoryIndexEntries(memoryDir));
197
+ if (broken.size === 0)
198
+ return [];
199
+ const keptLines = raw
200
+ .split(/\r?\n/)
201
+ .filter((line) => {
202
+ const match = line.match(/\[[^\]]+\]\(([^)]+)\)/);
203
+ if (!match)
204
+ return true;
205
+ const href = match[1]?.trim();
206
+ return href ? !broken.has(href) : true;
207
+ });
208
+ const next = keptLines.join('\n').replace(/\n*$/, '\n');
209
+ writeFileSync(entrypoint, next, 'utf-8');
210
+ return [...broken].sort();
211
+ }
212
+ catch {
213
+ return [];
214
+ }
215
+ }
216
+ export function buildConsolidationPrompt(memoryDir, sessionDir) {
217
+ return `# Dream: Memory Consolidation
218
+
219
+ You are performing a dream — a reflective pass over existing memory files. Improve the memory store so future sessions can orient quickly and trust what they read.
220
+
221
+ Memory directory: ${memoryDir}
222
+ Session transcripts: ${sessionDir}
223
+
224
+ The memory system prompt already defines the memory file format, allowed memory types, and what not to save. Follow that as the source of truth.
225
+
226
+ ## Phase 1 — Orient
227
+ - List the memory directory and inspect MEMORY.md first
228
+ - Skim existing topic files before creating anything new
229
+ - Prefer improving existing files over creating near-duplicates
230
+ - Validate every MEMORY.md link and repair or remove broken pointers
231
+
232
+ ## Phase 2 — Gather recent signal
233
+ - Review recent session transcripts only as needed
234
+ - Search transcripts narrowly for concrete terms instead of reading them exhaustively
235
+ - Look for drift, contradictions, stale wording, duplicate memories, and stale index entries
236
+
237
+ ## Phase 3 — Consolidate
238
+ - Merge duplicate or overlapping memories
239
+ - Update existing memories with clearer and more durable wording
240
+ - Convert relative dates like “yesterday” into absolute dates when they matter
241
+ - Remove contradicted, stale, or superseded facts
242
+ - Keep only durable information that will help in future conversations
243
+
244
+ ## Phase 4 — Prune and index
245
+ - Keep MEMORY.md as a concise index, not a content dump
246
+ - Each MEMORY.md entry should stay to one short line
247
+ - Remove pointers to stale or deleted memories
248
+ - Resolve contradictions between files instead of leaving both versions in place
249
+
250
+ ## Tooling constraints
251
+ - When using write or edit, always target absolute paths inside ${memoryDir}
252
+ - Do not use relative write/edit paths like MEMORY.md or user_identity.md because they resolve against the repo cwd, not the memory directory
253
+
254
+ ## Guardrails
255
+ - Only modify files inside the memory directory
256
+ - Do not edit project source files
257
+ - Use bash only for read-only inspection
258
+ - If the memory store is already in good shape, make no changes and say so
259
+
260
+ Return a short summary of what you consolidated, updated, pruned, or left unchanged.`;
261
+ }
262
+ function writeAudit(memoryDir, fields) {
263
+ const lines = Object.entries(fields)
264
+ .filter(([, value]) => value !== undefined)
265
+ .map(([key, value]) => `${key}: ${String(value)}`);
266
+ writeFileSync(getAuditPath(memoryDir), lines.join('\n') + '\n', 'utf-8');
267
+ }
268
+ function startDetachedDreamProcess(cwd, memoryDir, sessionDir, trigger, priorMtime, sessionCount) {
269
+ const cliPath = resolveCliPath();
270
+ const budgetModel = readBudgetMemoryModel();
271
+ const auditPath = getAuditPath(memoryDir);
272
+ const logPath = getLogPath(memoryDir);
273
+ if (!cliPath) {
274
+ writeAudit(memoryDir, {
275
+ timestamp: new Date().toISOString(),
276
+ status: 'skipped',
277
+ trigger,
278
+ reason: 'cli_path_not_found',
279
+ cwd,
280
+ memoryDir,
281
+ sessionDir,
282
+ sessionsSinceLastConsolidation: sessionCount,
283
+ });
284
+ return {
285
+ started: false,
286
+ status: 'skipped',
287
+ message: 'Dream skipped because the CLI path could not be resolved.',
288
+ };
289
+ }
290
+ const prompt = buildConsolidationPrompt(memoryDir, sessionDir);
291
+ const tmpPromptPath = join(tmpdir(), `lsd-memory-dream-${randomUUID()}.md`);
292
+ writeFileSync(tmpPromptPath, prompt, 'utf-8');
293
+ writeAudit(memoryDir, {
294
+ timestamp: new Date().toISOString(),
295
+ status: 'spawning',
296
+ trigger,
297
+ cwd,
298
+ memoryDir,
299
+ sessionDir,
300
+ sessionsSinceLastConsolidation: sessionCount,
301
+ cliPath,
302
+ model: budgetModel ?? 'default',
303
+ logPath,
304
+ });
305
+ const instruction = 'Perform a dream consolidation pass over the memory directory. Only update memory files and MEMORY.md if they genuinely need consolidation.';
306
+ const helperScript = String.raw `
307
+ const { spawn } = require('node:child_process');
308
+ const {
309
+ appendFileSync,
310
+ existsSync,
311
+ readdirSync,
312
+ readFileSync,
313
+ statSync,
314
+ unlinkSync,
315
+ utimesSync,
316
+ writeFileSync,
317
+ } = require('node:fs');
318
+ const { join, delimiter } = require('node:path');
319
+
320
+ const [cliPath, cwd, tmpPromptPath, auditPath, logPath, memoryDir, sessionDir, instruction, model, trigger, priorMtime, sessionCount] = process.argv.slice(1);
321
+ let finalized = false;
322
+ let pendingLogText = '';
323
+
324
+ function newestMemoryMtime(dir) {
325
+ try {
326
+ const names = readdirSync(dir, { recursive: true });
327
+ let newest = 0;
328
+ for (const name of names) {
329
+ const parts = String(name).split(/[\\/]/).filter(Boolean);
330
+ if (parts.some((part) => part.startsWith('.'))) continue;
331
+ const full = join(dir, String(name));
332
+ try {
333
+ const stat = statSync(full);
334
+ if (stat.isFile() && stat.mtimeMs > newest) newest = stat.mtimeMs;
335
+ } catch {}
336
+ }
337
+ return newest;
338
+ } catch {
339
+ return 0;
340
+ }
341
+ }
342
+
343
+ function isPathInsideDir(targetPath, dir) {
344
+ try {
345
+ const resolvedDir = require('node:path').resolve(dir);
346
+ const resolvedTarget = require('node:path').resolve(targetPath);
347
+ return resolvedTarget === resolvedDir || resolvedTarget.startsWith(resolvedDir + '/');
348
+ } catch {
349
+ return false;
350
+ }
351
+ }
352
+
353
+ function listBrokenMemoryRefs(dir) {
354
+ try {
355
+ const entrypoint = join(dir, 'MEMORY.md');
356
+ const raw = readFileSync(entrypoint, 'utf-8');
357
+ const broken = new Set();
358
+ const linkRe = /\[[^\]]+\]\(([^)]+)\)/g;
359
+ for (const match of raw.matchAll(linkRe)) {
360
+ const href = String(match[1] || '').trim();
361
+ if (!href || href.startsWith('#') || /^[a-z][a-z0-9+.-]*:/i.test(href)) continue;
362
+ const target = require('node:path').isAbsolute(href)
363
+ ? require('node:path').resolve(href)
364
+ : require('node:path').resolve(dir, href);
365
+ if (!isPathInsideDir(target, dir) || !existsSync(target)) broken.add(href);
366
+ }
367
+ return Array.from(broken).sort();
368
+ } catch {
369
+ return [];
370
+ }
371
+ }
372
+
373
+ function pruneBrokenMemoryRefs(dir) {
374
+ try {
375
+ const entrypoint = join(dir, 'MEMORY.md');
376
+ const raw = readFileSync(entrypoint, 'utf-8');
377
+ const broken = new Set(listBrokenMemoryRefs(dir));
378
+ if (broken.size === 0) return [];
379
+ const kept = raw
380
+ .split(/\r?\n/)
381
+ .filter((line) => {
382
+ const match = line.match(/\[[^\]]+\]\(([^)]+)\)/);
383
+ if (!match) return true;
384
+ const href = String(match[1] || '').trim();
385
+ return href ? !broken.has(href) : true;
386
+ });
387
+ writeFileSync(entrypoint, kept.join('\n').replace(/\n*$/, '\n'), 'utf-8');
388
+ return Array.from(broken).sort();
389
+ } catch {
390
+ return [];
391
+ }
392
+ }
393
+
394
+ function writeAudit(status, extra = []) {
395
+ try {
396
+ writeFileSync(auditPath, [
397
+ 'timestamp: ' + new Date().toISOString(),
398
+ 'status: ' + status,
399
+ 'trigger: ' + trigger,
400
+ 'cwd: ' + cwd,
401
+ 'memoryDir: ' + memoryDir,
402
+ 'sessionDir: ' + sessionDir,
403
+ 'sessionsSinceLastConsolidation: ' + sessionCount,
404
+ 'cliPath: ' + cliPath,
405
+ 'model: ' + (model || 'default'),
406
+ 'logPath: ' + logPath,
407
+ ...extra,
408
+ ].join('\n') + '\n', 'utf-8');
409
+ } catch {}
410
+ }
411
+
412
+ function rollbackLock() {
413
+ const lockPath = join(memoryDir, '.consolidate-lock');
414
+ try {
415
+ const prior = Number(priorMtime);
416
+ if (!Number.isFinite(prior) || prior <= 0) {
417
+ if (existsSync(lockPath)) unlinkSync(lockPath);
418
+ return;
419
+ }
420
+ writeFileSync(lockPath, '', 'utf-8');
421
+ const t = prior / 1000;
422
+ utimesSync(lockPath, t, t);
423
+ } catch {}
424
+ }
425
+
426
+ function flushLogText(text, force = false) {
427
+ pendingLogText += text;
428
+ const parts = pendingLogText.split(/\r?\n/);
429
+ pendingLogText = force ? '' : (parts.pop() ?? '');
430
+ const kept = parts.filter((line) => line.trim());
431
+ if (kept.length > 0) appendFileSync(logPath, kept.join('\n') + '\n');
432
+ }
433
+
434
+ function appendLog(chunk) {
435
+ try {
436
+ const text = Buffer.isBuffer(chunk) ? chunk.toString('utf-8') : String(chunk);
437
+ flushLogText(text, false);
438
+ } catch {}
439
+ }
440
+
441
+ function finalize(status, code, signal, completionReason) {
442
+ if (finalized) return;
443
+ finalized = true;
444
+ flushLogText('', true);
445
+ const beforeBrokenRefs = listBrokenMemoryRefs(memoryDir);
446
+ const prunedRefs = beforeBrokenRefs.length > 0 ? pruneBrokenMemoryRefs(memoryDir) : [];
447
+ const afterMtime = newestMemoryMtime(memoryDir);
448
+ const brokenRefs = listBrokenMemoryRefs(memoryDir);
449
+ const result = brokenRefs.length > 0
450
+ ? 'broken_memory_index'
451
+ : afterMtime > beforeMtime
452
+ ? 'updated_memory'
453
+ : 'no_memory_changes';
454
+ if (status !== 'finished') rollbackLock();
455
+ writeAudit(status, [
456
+ 'exitCode: ' + String(code),
457
+ 'signal: ' + String(signal),
458
+ 'result: ' + result,
459
+ 'brokenRefsPrunedCount: ' + String(prunedRefs.length),
460
+ ...(prunedRefs.length > 0 ? ['brokenRefsPruned: ' + prunedRefs.join(', ')] : []),
461
+ 'brokenRefsCount: ' + String(brokenRefs.length),
462
+ ...(brokenRefs.length > 0 ? ['brokenRefs: ' + brokenRefs.join(', ')] : []),
463
+ 'completionReason: ' + completionReason,
464
+ ]);
465
+ try { unlinkSync(tmpPromptPath); } catch {}
466
+ }
467
+
468
+ const beforeMtime = newestMemoryMtime(memoryDir);
469
+ const lockPath = join(memoryDir, '.consolidate-lock');
470
+ try { writeFileSync(lockPath, String(process.pid), 'utf-8'); } catch {}
471
+ writeFileSync(logPath, '', 'utf-8');
472
+ writeAudit('running');
473
+
474
+ const childArgs = [cliPath, 'headless'];
475
+ const bundledPaths = Array.from(
476
+ new Set(
477
+ [process.env.GSD_BUNDLED_EXTENSION_PATHS, process.env.LSD_BUNDLED_EXTENSION_PATHS]
478
+ .filter(Boolean)
479
+ .flatMap((value) => String(value).split(delimiter).map((entry) => entry.trim()).filter(Boolean)),
480
+ ),
481
+ );
482
+ for (const extensionPath of bundledPaths) childArgs.push('--extension', extensionPath);
483
+ if (model) childArgs.push('--model', model);
484
+ childArgs.push('--bare', '--context', tmpPromptPath, '--context-text', instruction);
485
+
486
+ const child = spawn(process.execPath, childArgs, {
487
+ cwd,
488
+ env: { ...process.env, LSD_MEMORY_DREAM: '1' },
489
+ stdio: ['ignore', 'pipe', 'pipe'],
490
+ });
491
+
492
+ const hardTimeout = setTimeout(() => {
493
+ finalize('failed', null, 'timeout', 'timeout');
494
+ try { child.kill('SIGTERM'); } catch {}
495
+ setTimeout(() => { try { child.kill('SIGKILL'); } catch {} }, 2000).unref();
496
+ }, 180000);
497
+ hardTimeout.unref();
498
+
499
+ child.stdout.on('data', appendLog);
500
+ child.stderr.on('data', appendLog);
501
+ child.on('error', (err) => {
502
+ appendLog(String(err && err.stack ? err.stack : err) + '\n');
503
+ clearTimeout(hardTimeout);
504
+ finalize('failed', null, 'spawn_error', String(err && err.message ? err.message : err));
505
+ });
506
+ child.on('exit', (code, signal) => {
507
+ clearTimeout(hardTimeout);
508
+ finalize(code === 0 ? 'finished' : 'failed', code, signal, 'child_exit');
509
+ });
510
+ `;
511
+ const proc = spawn(process.execPath, [
512
+ '-e',
513
+ helperScript,
514
+ cliPath,
515
+ cwd,
516
+ tmpPromptPath,
517
+ auditPath,
518
+ logPath,
519
+ memoryDir,
520
+ sessionDir,
521
+ instruction,
522
+ budgetModel ?? '',
523
+ trigger,
524
+ String(priorMtime),
525
+ String(sessionCount),
526
+ ], {
527
+ cwd,
528
+ detached: true,
529
+ stdio: 'ignore',
530
+ env: process.env,
531
+ });
532
+ proc.unref();
533
+ writeAudit(memoryDir, {
534
+ timestamp: new Date().toISOString(),
535
+ status: 'spawned',
536
+ trigger,
537
+ pid: proc.pid ?? 'unknown',
538
+ cwd,
539
+ memoryDir,
540
+ sessionDir,
541
+ sessionsSinceLastConsolidation: sessionCount,
542
+ cliPath,
543
+ model: budgetModel ?? 'default',
544
+ logPath,
545
+ });
546
+ setTimeout(() => {
547
+ try {
548
+ unlinkSync(tmpPromptPath);
549
+ }
550
+ catch {
551
+ // Best-effort cleanup only.
552
+ }
553
+ }, 180_000).unref();
554
+ return {
555
+ started: true,
556
+ status: 'started',
557
+ message: trigger === 'auto'
558
+ ? `Auto-dream started in the background (${sessionCount} sessions since the last consolidation).`
559
+ : 'Dream started in the background.',
560
+ };
561
+ }
562
+ export function startDream(ctx, options) {
563
+ if (process.env.LSD_MEMORY_EXTRACT === '1' || process.env.LSD_MEMORY_DREAM === '1') {
564
+ return {
565
+ started: false,
566
+ status: 'skipped',
567
+ message: 'Dream is unavailable from inside a maintenance worker.',
568
+ };
569
+ }
570
+ const trigger = options?.trigger ?? 'manual';
571
+ const memoryDir = getMemoryDir(ctx.cwd);
572
+ const sessionDir = ctx.sessionManager.getSessionDir();
573
+ const currentSessionFile = ctx.sessionManager.getSessionFile();
574
+ const sessionCount = options?.sessionCountHint ??
575
+ listSessionsTouchedSince(sessionDir, readLastConsolidatedAt(memoryDir), currentSessionFile).length;
576
+ const priorMtime = tryAcquireConsolidationLock(memoryDir);
577
+ if (priorMtime === null) {
578
+ return {
579
+ started: false,
580
+ status: 'busy',
581
+ message: 'A dream consolidation is already running.',
582
+ };
583
+ }
584
+ return startDetachedDreamProcess(ctx.cwd, memoryDir, sessionDir, trigger, priorMtime, sessionCount);
585
+ }
586
+ export function maybeStartAutoDream(ctx) {
587
+ if (process.env.CLAUDE_CODE_DISABLE_AUTO_MEMORY) {
588
+ return {
589
+ started: false,
590
+ status: 'skipped',
591
+ message: 'Auto-memory is disabled.',
592
+ };
593
+ }
594
+ if (process.env.LSD_MEMORY_EXTRACT === '1' || process.env.LSD_MEMORY_DREAM === '1') {
595
+ return {
596
+ started: false,
597
+ status: 'skipped',
598
+ message: 'Maintenance workers do not schedule auto-dream.',
599
+ };
600
+ }
601
+ const settings = readAutoDreamSettings(ctx.cwd);
602
+ if (!settings.enabled) {
603
+ return {
604
+ started: false,
605
+ status: 'skipped',
606
+ message: 'Auto-dream is disabled.',
607
+ };
608
+ }
609
+ const memoryDir = getMemoryDir(ctx.cwd);
610
+ const lastAt = readLastConsolidatedAt(memoryDir);
611
+ const hoursSince = lastAt > 0 ? (Date.now() - lastAt) / 3_600_000 : Number.POSITIVE_INFINITY;
612
+ if (hoursSince < settings.minHours) {
613
+ return {
614
+ started: false,
615
+ status: 'skipped',
616
+ message: `Only ${hoursSince.toFixed(1)}h since the last dream; need ${settings.minHours}h.`,
617
+ };
618
+ }
619
+ const sinceLastScan = Date.now() - lastAutoDreamScanAt;
620
+ if (sinceLastScan < SESSION_SCAN_INTERVAL_MS) {
621
+ return {
622
+ started: false,
623
+ status: 'skipped',
624
+ message: 'Auto-dream scan throttled.',
625
+ };
626
+ }
627
+ lastAutoDreamScanAt = Date.now();
628
+ const sessionFiles = listSessionsTouchedSince(ctx.sessionManager.getSessionDir(), lastAt, ctx.sessionManager.getSessionFile());
629
+ if (sessionFiles.length < settings.minSessions) {
630
+ return {
631
+ started: false,
632
+ status: 'skipped',
633
+ message: `Only ${sessionFiles.length} sessions since the last dream; need ${settings.minSessions}.`,
634
+ };
635
+ }
636
+ return startDream(ctx, {
637
+ trigger: 'auto',
638
+ sessionCountHint: sessionFiles.length,
639
+ });
640
+ }
641
+ export function formatDreamStatus(ctx) {
642
+ const memoryDir = getMemoryDir(ctx.cwd);
643
+ const settings = readAutoDreamSettings(ctx.cwd);
644
+ const audit = parseAuditFile(getAuditPath(memoryDir));
645
+ const lastAt = readLastConsolidatedAt(memoryDir);
646
+ const sessionsSince = listSessionsTouchedSince(ctx.sessionManager.getSessionDir(), lastAt, ctx.sessionManager.getSessionFile()).length;
647
+ const lines = [
648
+ 'Dream Status',
649
+ '',
650
+ `- Memory directory: ${memoryDir}`,
651
+ `- Auto-dream enabled: ${settings.enabled ? 'yes' : 'no'}`,
652
+ `- Thresholds: ${settings.minHours}h / ${settings.minSessions} sessions`,
653
+ `- Last consolidated at: ${lastAt > 0 ? new Date(lastAt).toISOString() : 'never'}`,
654
+ `- Sessions since last consolidation: ${sessionsSince}`,
655
+ `- Dream currently running: ${isConsolidationInProgress(memoryDir) ? 'yes' : 'no'}`,
656
+ ];
657
+ if (Object.keys(audit).length > 0) {
658
+ lines.push('', 'Last recorded dream run:');
659
+ for (const key of [
660
+ 'timestamp',
661
+ 'status',
662
+ 'trigger',
663
+ 'result',
664
+ 'brokenRefsCount',
665
+ 'brokenRefs',
666
+ 'completionReason',
667
+ 'pid',
668
+ 'model',
669
+ 'logPath',
670
+ ]) {
671
+ if (audit[key]) {
672
+ lines.push(`- ${key}: ${audit[key]}`);
673
+ }
674
+ }
675
+ }
676
+ else {
677
+ lines.push('', '- No dream audit file found yet.');
678
+ }
679
+ return lines.join('\n');
680
+ }
681
+ export function isMaintenanceModeToolAllowed(toolName, input, cwd) {
682
+ if (!(process.env.LSD_MEMORY_EXTRACT === '1' || process.env.LSD_MEMORY_DREAM === '1')) {
683
+ return true;
684
+ }
685
+ if (toolName === 'read' || toolName === 'grep' || toolName === 'find' || toolName === 'ls') {
686
+ return true;
687
+ }
688
+ if ((toolName === 'write' || toolName === 'edit') && input?.path && cwd) {
689
+ const targetPath = isAbsolute(input.path) ? resolve(input.path) : resolve(cwd, input.path);
690
+ return isPathInsideDir(targetPath, getMemoryDir(cwd));
691
+ }
692
+ if (toolName === 'bash' && input?.command) {
693
+ return READ_ONLY_BASH_RE.test(input.command);
694
+ }
695
+ return false;
696
+ }
697
+ export const __testing = {
698
+ listBrokenMemoryIndexEntries,
699
+ pruneBrokenMemoryIndexEntries,
700
+ parseAutoDreamSettings,
701
+ listSessionsTouchedSince,
702
+ readJsonFile,
703
+ };