gencode-ai 0.1.1 → 0.1.2
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/.gencode/settings.local.json +7 -0
- package/README.md +11 -11
- package/dist/agent/agent.d.ts +42 -1
- package/dist/agent/agent.d.ts.map +1 -1
- package/dist/agent/agent.js +82 -15
- package/dist/agent/agent.js.map +1 -1
- package/dist/cli/components/App.d.ts +8 -1
- package/dist/cli/components/App.d.ts.map +1 -1
- package/dist/cli/components/App.js +231 -29
- package/dist/cli/components/App.js.map +1 -1
- package/dist/cli/components/CommandSuggestions.d.ts.map +1 -1
- package/dist/cli/components/CommandSuggestions.js +2 -0
- package/dist/cli/components/CommandSuggestions.js.map +1 -1
- package/dist/cli/components/Header.d.ts +1 -1
- package/dist/cli/components/Header.d.ts.map +1 -1
- package/dist/cli/components/Header.js +4 -6
- package/dist/cli/components/Header.js.map +1 -1
- package/dist/cli/components/Logo.d.ts +1 -0
- package/dist/cli/components/Logo.d.ts.map +1 -1
- package/dist/cli/components/Logo.js +16 -3
- package/dist/cli/components/Logo.js.map +1 -1
- package/dist/cli/components/Messages.d.ts +4 -4
- package/dist/cli/components/Messages.d.ts.map +1 -1
- package/dist/cli/components/Messages.js +51 -25
- package/dist/cli/components/Messages.js.map +1 -1
- package/dist/cli/components/PermissionPrompt.d.ts +60 -0
- package/dist/cli/components/PermissionPrompt.d.ts.map +1 -0
- package/dist/cli/components/PermissionPrompt.js +192 -0
- package/dist/cli/components/PermissionPrompt.js.map +1 -0
- package/dist/cli/components/ProviderManager.js +3 -3
- package/dist/cli/components/ProviderManager.js.map +1 -1
- package/dist/cli/components/Spinner.d.ts +7 -2
- package/dist/cli/components/Spinner.d.ts.map +1 -1
- package/dist/cli/components/Spinner.js +116 -25
- package/dist/cli/components/Spinner.js.map +1 -1
- package/dist/cli/components/TodoList.d.ts +7 -0
- package/dist/cli/components/TodoList.d.ts.map +1 -0
- package/dist/cli/components/TodoList.js +34 -0
- package/dist/cli/components/TodoList.js.map +1 -0
- package/dist/cli/components/index.d.ts +1 -0
- package/dist/cli/components/index.d.ts.map +1 -1
- package/dist/cli/components/index.js +1 -0
- package/dist/cli/components/index.js.map +1 -1
- package/dist/cli/index.js +47 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/config/index.d.ts +13 -4
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +18 -3
- package/dist/config/index.js.map +1 -1
- package/dist/config/levels.d.ts +49 -0
- package/dist/config/levels.d.ts.map +1 -0
- package/dist/config/levels.js +222 -0
- package/dist/config/levels.js.map +1 -0
- package/dist/config/loader.d.ts +46 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +153 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/manager.d.ts +115 -15
- package/dist/config/manager.d.ts.map +1 -1
- package/dist/config/manager.js +260 -34
- package/dist/config/manager.js.map +1 -1
- package/dist/config/manager.test.d.ts +5 -0
- package/dist/config/manager.test.d.ts.map +1 -0
- package/dist/config/manager.test.js +192 -0
- package/dist/config/manager.test.js.map +1 -0
- package/dist/config/merger.d.ts +56 -0
- package/dist/config/merger.d.ts.map +1 -0
- package/dist/config/merger.js +177 -0
- package/dist/config/merger.js.map +1 -0
- package/dist/config/test-utils.d.ts +24 -0
- package/dist/config/test-utils.d.ts.map +1 -0
- package/dist/config/test-utils.js +55 -0
- package/dist/config/test-utils.js.map +1 -0
- package/dist/config/types.d.ts +78 -9
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +52 -2
- package/dist/config/types.js.map +1 -1
- package/dist/memory/import-resolver.d.ts +46 -0
- package/dist/memory/import-resolver.d.ts.map +1 -0
- package/dist/memory/import-resolver.js +117 -0
- package/dist/memory/import-resolver.js.map +1 -0
- package/dist/memory/index.d.ts +7 -6
- package/dist/memory/index.d.ts.map +1 -1
- package/dist/memory/index.js +7 -5
- package/dist/memory/index.js.map +1 -1
- package/dist/memory/init-prompt.d.ts +22 -0
- package/dist/memory/init-prompt.d.ts.map +1 -0
- package/dist/memory/init-prompt.js +103 -0
- package/dist/memory/init-prompt.js.map +1 -0
- package/dist/memory/memory-manager.d.ts +119 -0
- package/dist/memory/memory-manager.d.ts.map +1 -0
- package/dist/memory/memory-manager.js +587 -0
- package/dist/memory/memory-manager.js.map +1 -0
- package/dist/memory/rules-parser.d.ts +38 -0
- package/dist/memory/rules-parser.d.ts.map +1 -0
- package/dist/memory/rules-parser.js +69 -0
- package/dist/memory/rules-parser.js.map +1 -0
- package/dist/memory/test-utils.d.ts +20 -0
- package/dist/memory/test-utils.d.ts.map +1 -0
- package/dist/memory/test-utils.js +44 -0
- package/dist/memory/test-utils.js.map +1 -0
- package/dist/memory/types.d.ts +70 -63
- package/dist/memory/types.d.ts.map +1 -1
- package/dist/memory/types.js +42 -2
- package/dist/memory/types.js.map +1 -1
- package/dist/permissions/audit.d.ts +82 -0
- package/dist/permissions/audit.d.ts.map +1 -0
- package/dist/permissions/audit.js +229 -0
- package/dist/permissions/audit.js.map +1 -0
- package/dist/permissions/index.d.ts +11 -1
- package/dist/permissions/index.d.ts.map +1 -1
- package/dist/permissions/index.js +15 -0
- package/dist/permissions/index.js.map +1 -1
- package/dist/permissions/manager.d.ts +149 -13
- package/dist/permissions/manager.d.ts.map +1 -1
- package/dist/permissions/manager.js +480 -35
- package/dist/permissions/manager.js.map +1 -1
- package/dist/permissions/manager.test.d.ts +5 -0
- package/dist/permissions/manager.test.d.ts.map +1 -0
- package/dist/permissions/manager.test.js +213 -0
- package/dist/permissions/manager.test.js.map +1 -0
- package/dist/permissions/persistence.d.ts +74 -0
- package/dist/permissions/persistence.d.ts.map +1 -0
- package/dist/permissions/persistence.js +248 -0
- package/dist/permissions/persistence.js.map +1 -0
- package/dist/permissions/persistence.test.d.ts +5 -0
- package/dist/permissions/persistence.test.d.ts.map +1 -0
- package/dist/permissions/persistence.test.js +171 -0
- package/dist/permissions/persistence.test.js.map +1 -0
- package/dist/permissions/prompt-matcher.d.ts +64 -0
- package/dist/permissions/prompt-matcher.d.ts.map +1 -0
- package/dist/permissions/prompt-matcher.js +415 -0
- package/dist/permissions/prompt-matcher.js.map +1 -0
- package/dist/permissions/prompt-matcher.test.d.ts +5 -0
- package/dist/permissions/prompt-matcher.test.d.ts.map +1 -0
- package/dist/permissions/prompt-matcher.test.js +107 -0
- package/dist/permissions/prompt-matcher.test.js.map +1 -0
- package/dist/permissions/types.d.ts +157 -0
- package/dist/permissions/types.d.ts.map +1 -1
- package/dist/permissions/types.js +43 -8
- package/dist/permissions/types.js.map +1 -1
- package/dist/prompts/index.d.ts +92 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +241 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/tools/builtin/bash.d.ts.map +1 -1
- package/dist/tools/builtin/bash.js +2 -1
- package/dist/tools/builtin/bash.js.map +1 -1
- package/dist/tools/builtin/edit.d.ts.map +1 -1
- package/dist/tools/builtin/edit.js +2 -1
- package/dist/tools/builtin/edit.js.map +1 -1
- package/dist/tools/builtin/glob.d.ts.map +1 -1
- package/dist/tools/builtin/glob.js +2 -1
- package/dist/tools/builtin/glob.js.map +1 -1
- package/dist/tools/builtin/grep.d.ts.map +1 -1
- package/dist/tools/builtin/grep.js +2 -1
- package/dist/tools/builtin/grep.js.map +1 -1
- package/dist/tools/builtin/read.d.ts.map +1 -1
- package/dist/tools/builtin/read.js +2 -1
- package/dist/tools/builtin/read.js.map +1 -1
- package/dist/tools/builtin/todowrite.d.ts +15 -0
- package/dist/tools/builtin/todowrite.d.ts.map +1 -0
- package/dist/tools/builtin/todowrite.js +88 -0
- package/dist/tools/builtin/todowrite.js.map +1 -0
- package/dist/tools/builtin/webfetch.d.ts.map +1 -1
- package/dist/tools/builtin/webfetch.js +2 -5
- package/dist/tools/builtin/webfetch.js.map +1 -1
- package/dist/tools/builtin/websearch.d.ts.map +1 -1
- package/dist/tools/builtin/websearch.js +2 -16
- package/dist/tools/builtin/websearch.js.map +1 -1
- package/dist/tools/builtin/write.d.ts.map +1 -1
- package/dist/tools/builtin/write.js +2 -1
- package/dist/tools/builtin/write.js.map +1 -1
- package/dist/tools/index.d.ts +7 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +4 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/types.d.ts +22 -0
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/types.js +8 -0
- package/dist/tools/types.js.map +1 -1
- package/docs/config-system-comparison.md +707 -0
- package/docs/memory-system.md +238 -0
- package/docs/permissions.md +368 -0
- package/docs/proposals/0005-todo-system.md +350 -85
- package/docs/proposals/0006-memory-system.md +11 -10
- package/docs/proposals/0012-ask-user-question.md +941 -206
- package/docs/proposals/0023-permission-enhancements.md +61 -2
- package/docs/proposals/0041-configuration-system.md +33 -2
- package/docs/proposals/0042-prompt-optimization.md +866 -0
- package/docs/proposals/README.md +6 -5
- package/jest.config.js +26 -0
- package/package.json +8 -2
- package/src/agent/agent.ts +111 -16
- package/src/cli/components/App.tsx +309 -36
- package/src/cli/components/CommandSuggestions.tsx +2 -0
- package/src/cli/components/Header.tsx +11 -17
- package/src/cli/components/Logo.tsx +76 -9
- package/src/cli/components/Messages.tsx +73 -53
- package/src/cli/components/PermissionPrompt.tsx +388 -0
- package/src/cli/components/ProviderManager.tsx +5 -5
- package/src/cli/components/Spinner.tsx +138 -25
- package/src/cli/components/TodoList.tsx +54 -0
- package/src/cli/components/index.ts +6 -0
- package/src/cli/index.tsx +54 -6
- package/src/config/index.ts +78 -4
- package/src/config/levels.test.ts +163 -0
- package/src/config/levels.ts +285 -0
- package/src/config/loader.test.ts +120 -0
- package/src/config/loader.ts +178 -0
- package/src/config/manager.test.ts +215 -0
- package/src/config/manager.ts +328 -40
- package/src/config/merger.test.ts +360 -0
- package/src/config/merger.ts +221 -0
- package/src/config/test-utils.ts +79 -0
- package/src/config/types.ts +152 -9
- package/src/memory/import-resolver.test.ts +117 -0
- package/src/memory/import-resolver.ts +149 -0
- package/src/memory/index.ts +11 -0
- package/src/memory/init-prompt.ts +113 -0
- package/src/memory/memory-manager.test.ts +198 -0
- package/src/memory/memory-manager.ts +716 -0
- package/src/memory/rules-parser.test.ts +182 -0
- package/src/memory/rules-parser.ts +82 -0
- package/src/memory/test-utils.ts +60 -0
- package/src/memory/types.ts +119 -0
- package/src/permissions/audit.ts +284 -0
- package/src/permissions/index.ts +20 -1
- package/src/permissions/manager.test.ts +260 -0
- package/src/permissions/manager.ts +592 -40
- package/src/permissions/persistence.test.ts +220 -0
- package/src/permissions/persistence.ts +301 -0
- package/src/permissions/prompt-matcher.test.ts +213 -0
- package/src/permissions/prompt-matcher.ts +472 -0
- package/src/permissions/types.ts +236 -8
- package/src/prompts/index.test.ts +279 -0
- package/src/prompts/index.ts +306 -0
- package/src/prompts/system/anthropic.txt +29 -0
- package/src/prompts/system/base.txt +124 -0
- package/src/prompts/system/gemini.txt +35 -0
- package/src/prompts/system/generic.txt +128 -0
- package/src/prompts/system/openai.txt +29 -0
- package/src/prompts/tools/bash.txt +60 -0
- package/src/prompts/tools/edit.txt +29 -0
- package/src/prompts/tools/glob.txt +35 -0
- package/src/prompts/tools/grep.txt +43 -0
- package/src/prompts/tools/read.txt +22 -0
- package/src/prompts/tools/todowrite.txt +71 -0
- package/src/prompts/tools/webfetch.txt +34 -0
- package/src/prompts/tools/websearch.txt +41 -0
- package/src/prompts/tools/write.txt +23 -0
- package/src/tools/builtin/bash.ts +2 -1
- package/src/tools/builtin/edit.ts +2 -1
- package/src/tools/builtin/glob.ts +2 -1
- package/src/tools/builtin/grep.ts +2 -1
- package/src/tools/builtin/read.ts +2 -1
- package/src/tools/builtin/todowrite.ts +102 -0
- package/src/tools/builtin/webfetch.ts +2 -5
- package/src/tools/builtin/websearch.ts +2 -16
- package/src/tools/builtin/write.ts +2 -1
- package/src/tools/index.ts +4 -0
- package/src/tools/types.ts +12 -0
- package/tsconfig.json +1 -1
|
@@ -0,0 +1,716 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory Manager - Core memory system implementation
|
|
3
|
+
*
|
|
4
|
+
* Implements Claude Code compatible memory loading with merge semantics:
|
|
5
|
+
* At each level, both .gencode and .claude directories are loaded.
|
|
6
|
+
* Content from .gencode appears later in the context (higher priority for LLM).
|
|
7
|
+
*
|
|
8
|
+
* Loading order within each level:
|
|
9
|
+
* 1. .claude files first (lower priority - LLM sees earlier)
|
|
10
|
+
* 2. .gencode files second (higher priority - LLM sees later)
|
|
11
|
+
*
|
|
12
|
+
* Level loading order:
|
|
13
|
+
* 1. Enterprise (system-wide managed, enforced)
|
|
14
|
+
* 2. User (~/.gencode/ + ~/.claude/)
|
|
15
|
+
* 3. User Rules
|
|
16
|
+
* 4. Extra (GENCODE_CONFIG_DIRS)
|
|
17
|
+
* 5. Project (recursive upward search)
|
|
18
|
+
* 6. Project Rules
|
|
19
|
+
* 7. Local (*.local.md files)
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import * as fs from 'fs/promises';
|
|
23
|
+
import * as path from 'path';
|
|
24
|
+
import * as os from 'os';
|
|
25
|
+
import { glob } from 'glob';
|
|
26
|
+
import { ImportResolver } from './import-resolver.js';
|
|
27
|
+
import { parseRuleFrontmatter, activateRules } from './rules-parser.js';
|
|
28
|
+
import type {
|
|
29
|
+
MemoryConfig,
|
|
30
|
+
MemoryFile,
|
|
31
|
+
MemoryRule,
|
|
32
|
+
LoadedMemory,
|
|
33
|
+
MemoryLoadOptions,
|
|
34
|
+
MemoryLevel,
|
|
35
|
+
MemoryNamespace,
|
|
36
|
+
MemorySource,
|
|
37
|
+
} from './types.js';
|
|
38
|
+
import { DEFAULT_MEMORY_CONFIG } from './types.js';
|
|
39
|
+
import { getManagedPaths, GENCODE_CONFIG_DIRS_ENV } from '../config/types.js';
|
|
40
|
+
|
|
41
|
+
export class MemoryManager {
|
|
42
|
+
private config: MemoryConfig;
|
|
43
|
+
private importResolver: ImportResolver;
|
|
44
|
+
private loadedMemory: LoadedMemory | null = null;
|
|
45
|
+
|
|
46
|
+
constructor(config?: Partial<MemoryConfig>) {
|
|
47
|
+
this.config = { ...DEFAULT_MEMORY_CONFIG, ...config };
|
|
48
|
+
// Create import resolver with the config
|
|
49
|
+
this.importResolver = new ImportResolver(this.config);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Load all memory files for the given working directory
|
|
54
|
+
*/
|
|
55
|
+
async load(options: MemoryLoadOptions): Promise<LoadedMemory> {
|
|
56
|
+
const { cwd, currentFile } = options;
|
|
57
|
+
const files: MemoryFile[] = [];
|
|
58
|
+
const rules: MemoryRule[] = [];
|
|
59
|
+
const errors: string[] = [];
|
|
60
|
+
const sources: MemorySource[] = [];
|
|
61
|
+
let totalSize = 0;
|
|
62
|
+
|
|
63
|
+
this.importResolver.reset();
|
|
64
|
+
const projectRoot = await this.findProjectRoot(cwd);
|
|
65
|
+
this.importResolver.setProjectRoot(projectRoot);
|
|
66
|
+
|
|
67
|
+
// 1. Load enterprise memory (system-wide, enforced)
|
|
68
|
+
const enterpriseFiles = await this.loadEnterpriseMemory();
|
|
69
|
+
for (const file of enterpriseFiles) {
|
|
70
|
+
files.push(file);
|
|
71
|
+
totalSize += file.content.length;
|
|
72
|
+
sources.push({
|
|
73
|
+
level: 'enterprise',
|
|
74
|
+
namespace: file.namespace,
|
|
75
|
+
path: file.path,
|
|
76
|
+
type: 'file',
|
|
77
|
+
size: file.content.length,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// 2. Load user-level memory (both claude and gencode)
|
|
82
|
+
const userFiles = await this.loadUserMemory();
|
|
83
|
+
for (const file of userFiles) {
|
|
84
|
+
if (totalSize + file.content.length <= this.config.maxTotalSize) {
|
|
85
|
+
files.push(file);
|
|
86
|
+
totalSize += file.content.length;
|
|
87
|
+
sources.push({
|
|
88
|
+
level: 'user',
|
|
89
|
+
namespace: file.namespace,
|
|
90
|
+
path: file.path,
|
|
91
|
+
type: 'file',
|
|
92
|
+
size: file.content.length,
|
|
93
|
+
});
|
|
94
|
+
} else {
|
|
95
|
+
errors.push(`Skipped ${file.path}: would exceed max total size`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// 3. Load user-level rules (both claude and gencode)
|
|
100
|
+
const userRules = await this.loadUserRules();
|
|
101
|
+
for (const rule of userRules) {
|
|
102
|
+
rules.push(rule);
|
|
103
|
+
sources.push({
|
|
104
|
+
level: 'user-rules',
|
|
105
|
+
namespace: rule.namespace,
|
|
106
|
+
path: rule.path,
|
|
107
|
+
type: 'rule',
|
|
108
|
+
size: rule.content.length,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 4. Load extra config dirs memory
|
|
113
|
+
const extraFiles = await this.loadExtraMemory();
|
|
114
|
+
for (const file of extraFiles) {
|
|
115
|
+
if (totalSize + file.content.length <= this.config.maxTotalSize) {
|
|
116
|
+
files.push(file);
|
|
117
|
+
totalSize += file.content.length;
|
|
118
|
+
sources.push({
|
|
119
|
+
level: 'extra',
|
|
120
|
+
namespace: file.namespace,
|
|
121
|
+
path: file.path,
|
|
122
|
+
type: 'file',
|
|
123
|
+
size: file.content.length,
|
|
124
|
+
});
|
|
125
|
+
} else {
|
|
126
|
+
errors.push(`Skipped ${file.path}: would exceed max total size`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// 5. Load project-level memory (both claude and gencode, recursive upward)
|
|
131
|
+
const projectFiles = await this.loadProjectMemory(cwd, projectRoot);
|
|
132
|
+
for (const file of projectFiles) {
|
|
133
|
+
if (totalSize + file.content.length <= this.config.maxTotalSize) {
|
|
134
|
+
files.push(file);
|
|
135
|
+
totalSize += file.content.length;
|
|
136
|
+
sources.push({
|
|
137
|
+
level: 'project',
|
|
138
|
+
namespace: file.namespace,
|
|
139
|
+
path: file.path,
|
|
140
|
+
type: 'file',
|
|
141
|
+
size: file.content.length,
|
|
142
|
+
});
|
|
143
|
+
} else {
|
|
144
|
+
errors.push(`Skipped ${file.path}: would exceed max total size`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// 6. Load project-level rules (both claude and gencode)
|
|
149
|
+
const projectRules = await this.loadProjectRules(projectRoot);
|
|
150
|
+
for (const rule of projectRules) {
|
|
151
|
+
rules.push(rule);
|
|
152
|
+
sources.push({
|
|
153
|
+
level: 'project-rules',
|
|
154
|
+
namespace: rule.namespace,
|
|
155
|
+
path: rule.path,
|
|
156
|
+
type: 'rule',
|
|
157
|
+
size: rule.content.length,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// 7. Load local memory (both claude and gencode)
|
|
162
|
+
const localFiles = await this.loadLocalMemory(projectRoot);
|
|
163
|
+
for (const file of localFiles) {
|
|
164
|
+
if (totalSize + file.content.length <= this.config.maxTotalSize) {
|
|
165
|
+
files.push(file);
|
|
166
|
+
totalSize += file.content.length;
|
|
167
|
+
sources.push({
|
|
168
|
+
level: 'local',
|
|
169
|
+
namespace: file.namespace,
|
|
170
|
+
path: file.path,
|
|
171
|
+
type: 'file',
|
|
172
|
+
size: file.content.length,
|
|
173
|
+
});
|
|
174
|
+
} else {
|
|
175
|
+
errors.push(`Skipped ${file.path}: would exceed max total size`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Activate rules based on current file
|
|
180
|
+
const activatedRules = activateRules(rules, currentFile);
|
|
181
|
+
|
|
182
|
+
// Build combined context
|
|
183
|
+
const context = this.buildContext(files, activatedRules);
|
|
184
|
+
|
|
185
|
+
this.loadedMemory = {
|
|
186
|
+
files,
|
|
187
|
+
rules: activatedRules,
|
|
188
|
+
totalSize,
|
|
189
|
+
context,
|
|
190
|
+
errors,
|
|
191
|
+
sources,
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
return this.loadedMemory;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Get the current loaded memory
|
|
199
|
+
*/
|
|
200
|
+
getLoaded(): LoadedMemory | null {
|
|
201
|
+
return this.loadedMemory;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Check if any memory is loaded
|
|
206
|
+
*/
|
|
207
|
+
hasMemory(): boolean {
|
|
208
|
+
return (
|
|
209
|
+
this.loadedMemory !== null &&
|
|
210
|
+
(this.loadedMemory.files.length > 0 || this.loadedMemory.rules.length > 0)
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Build combined context string for system prompt injection
|
|
216
|
+
*/
|
|
217
|
+
private buildContext(files: MemoryFile[], rules: MemoryRule[]): string {
|
|
218
|
+
const parts: string[] = [];
|
|
219
|
+
|
|
220
|
+
// Add regular memory files
|
|
221
|
+
for (const file of files) {
|
|
222
|
+
const label = this.getLevelLabel(file.level, file.namespace);
|
|
223
|
+
const enforced = file.enforced ? ' [ENFORCED]' : '';
|
|
224
|
+
parts.push(`Contents of ${file.path} (${label}${enforced}):\n\n${file.content}`);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Add active rules
|
|
228
|
+
const activeRules = rules.filter((r) => r.isActive);
|
|
229
|
+
for (const rule of activeRules) {
|
|
230
|
+
const patterns =
|
|
231
|
+
rule.patterns.length > 0 ? ` (applies to: ${rule.patterns.join(', ')})` : '';
|
|
232
|
+
parts.push(`Rule from ${rule.path}${patterns}:\n\n${rule.content}`);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (parts.length === 0) {
|
|
236
|
+
return '';
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return parts.join('\n\n---\n\n');
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
private getLevelLabel(level: MemoryLevel, namespace: MemoryNamespace): string {
|
|
243
|
+
const levelLabels: Record<MemoryLevel, string> = {
|
|
244
|
+
enterprise: 'enterprise policy',
|
|
245
|
+
user: "user's private global instructions",
|
|
246
|
+
'user-rules': 'user rules',
|
|
247
|
+
extra: 'extra config',
|
|
248
|
+
project: 'project instructions',
|
|
249
|
+
'project-rules': 'project rules',
|
|
250
|
+
local: 'local personal notes',
|
|
251
|
+
};
|
|
252
|
+
const namespaceLabel = namespace === 'gencode' ? 'gencode' : namespace === 'claude' ? 'claude' : 'extra';
|
|
253
|
+
return `${levelLabels[level]} - ${namespaceLabel}`;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Load enterprise-level memory files
|
|
258
|
+
*/
|
|
259
|
+
private async loadEnterpriseMemory(): Promise<MemoryFile[]> {
|
|
260
|
+
const files: MemoryFile[] = [];
|
|
261
|
+
const managedPaths = getManagedPaths();
|
|
262
|
+
|
|
263
|
+
// Load Claude first (lower priority)
|
|
264
|
+
const claudeFile = await this.loadFile(
|
|
265
|
+
path.join(managedPaths.claude, this.config.claudeFilename),
|
|
266
|
+
'enterprise',
|
|
267
|
+
'claude'
|
|
268
|
+
);
|
|
269
|
+
if (claudeFile) {
|
|
270
|
+
claudeFile.enforced = true;
|
|
271
|
+
files.push(claudeFile);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Load GenCode second (higher priority)
|
|
275
|
+
const gencodeFile = await this.loadFile(
|
|
276
|
+
path.join(managedPaths.gencode, this.config.gencodeFilename),
|
|
277
|
+
'enterprise',
|
|
278
|
+
'gencode'
|
|
279
|
+
);
|
|
280
|
+
if (gencodeFile) {
|
|
281
|
+
gencodeFile.enforced = true;
|
|
282
|
+
files.push(gencodeFile);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return files;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Load user-level memory files (both claude and gencode)
|
|
290
|
+
*/
|
|
291
|
+
private async loadUserMemory(): Promise<MemoryFile[]> {
|
|
292
|
+
const home = os.homedir();
|
|
293
|
+
const files: MemoryFile[] = [];
|
|
294
|
+
|
|
295
|
+
// Load Claude first (lower priority)
|
|
296
|
+
const claudeFile = await this.loadFile(
|
|
297
|
+
path.join(home, this.config.claudeDir, this.config.claudeFilename),
|
|
298
|
+
'user',
|
|
299
|
+
'claude'
|
|
300
|
+
);
|
|
301
|
+
if (claudeFile) files.push(claudeFile);
|
|
302
|
+
|
|
303
|
+
// Load GenCode second (higher priority)
|
|
304
|
+
const gencodeFile = await this.loadFile(
|
|
305
|
+
path.join(home, this.config.gencodeDir, this.config.gencodeFilename),
|
|
306
|
+
'user',
|
|
307
|
+
'gencode'
|
|
308
|
+
);
|
|
309
|
+
if (gencodeFile) files.push(gencodeFile);
|
|
310
|
+
|
|
311
|
+
return files;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Load user-level rules (both claude and gencode)
|
|
316
|
+
*/
|
|
317
|
+
private async loadUserRules(): Promise<MemoryRule[]> {
|
|
318
|
+
const home = os.homedir();
|
|
319
|
+
const rules: MemoryRule[] = [];
|
|
320
|
+
|
|
321
|
+
// Load Claude rules first (lower priority)
|
|
322
|
+
const claudeRulesDir = path.join(home, this.config.claudeDir, this.config.rulesDir);
|
|
323
|
+
const claudeRules = await this.loadRulesFromDir(claudeRulesDir, 'user-rules', 'claude');
|
|
324
|
+
rules.push(...claudeRules);
|
|
325
|
+
|
|
326
|
+
// Load GenCode rules second (higher priority)
|
|
327
|
+
const gencodeRulesDir = path.join(home, this.config.gencodeDir, this.config.rulesDir);
|
|
328
|
+
const gencodeRules = await this.loadRulesFromDir(gencodeRulesDir, 'user-rules', 'gencode');
|
|
329
|
+
rules.push(...gencodeRules);
|
|
330
|
+
|
|
331
|
+
return rules;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Load extra config dirs memory
|
|
336
|
+
*/
|
|
337
|
+
private async loadExtraMemory(): Promise<MemoryFile[]> {
|
|
338
|
+
const extraDirs = this.parseExtraConfigDirs();
|
|
339
|
+
const files: MemoryFile[] = [];
|
|
340
|
+
|
|
341
|
+
for (const dir of extraDirs) {
|
|
342
|
+
// Try CLAUDE.md
|
|
343
|
+
const claudeFile = await this.loadFile(
|
|
344
|
+
path.join(dir, this.config.claudeFilename),
|
|
345
|
+
'extra',
|
|
346
|
+
'extra'
|
|
347
|
+
);
|
|
348
|
+
if (claudeFile) files.push(claudeFile);
|
|
349
|
+
|
|
350
|
+
// Try AGENT.md
|
|
351
|
+
const gencodeFile = await this.loadFile(
|
|
352
|
+
path.join(dir, this.config.gencodeFilename),
|
|
353
|
+
'extra',
|
|
354
|
+
'extra'
|
|
355
|
+
);
|
|
356
|
+
if (gencodeFile) files.push(gencodeFile);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return files;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Parse GENCODE_CONFIG_DIRS environment variable
|
|
364
|
+
*/
|
|
365
|
+
private parseExtraConfigDirs(): string[] {
|
|
366
|
+
const value = process.env[GENCODE_CONFIG_DIRS_ENV];
|
|
367
|
+
if (!value) return [];
|
|
368
|
+
|
|
369
|
+
return value
|
|
370
|
+
.split(':')
|
|
371
|
+
.map((dir) => dir.trim())
|
|
372
|
+
.filter((dir) => dir.length > 0)
|
|
373
|
+
.map((dir) => dir.replace(/^~/, os.homedir()));
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Load project-level memory files (both claude and gencode)
|
|
378
|
+
*/
|
|
379
|
+
private async loadProjectMemory(cwd: string, projectRoot: string): Promise<MemoryFile[]> {
|
|
380
|
+
const files: MemoryFile[] = [];
|
|
381
|
+
|
|
382
|
+
// Load from project root - Claude files first
|
|
383
|
+
const claudeCandidates = [
|
|
384
|
+
path.join(projectRoot, this.config.claudeFilename),
|
|
385
|
+
path.join(projectRoot, this.config.claudeDir, this.config.claudeFilename),
|
|
386
|
+
];
|
|
387
|
+
|
|
388
|
+
for (const filePath of claudeCandidates) {
|
|
389
|
+
const file = await this.loadFile(filePath, 'project', 'claude');
|
|
390
|
+
if (file) {
|
|
391
|
+
files.push(file);
|
|
392
|
+
break; // Only load one claude file
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Load from project root - GenCode files second
|
|
397
|
+
const gencodeCandidates = [
|
|
398
|
+
path.join(projectRoot, this.config.gencodeFilename),
|
|
399
|
+
path.join(projectRoot, this.config.gencodeDir, this.config.gencodeFilename),
|
|
400
|
+
];
|
|
401
|
+
|
|
402
|
+
for (const filePath of gencodeCandidates) {
|
|
403
|
+
const file = await this.loadFile(filePath, 'project', 'gencode');
|
|
404
|
+
if (file) {
|
|
405
|
+
files.push(file);
|
|
406
|
+
break; // Only load one gencode file
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
return files;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Load project-level rules (both claude and gencode)
|
|
415
|
+
*/
|
|
416
|
+
private async loadProjectRules(projectRoot: string): Promise<MemoryRule[]> {
|
|
417
|
+
const rules: MemoryRule[] = [];
|
|
418
|
+
|
|
419
|
+
// Load Claude rules first (lower priority)
|
|
420
|
+
const claudeRulesDir = path.join(projectRoot, this.config.claudeDir, this.config.rulesDir);
|
|
421
|
+
const claudeRules = await this.loadRulesFromDir(claudeRulesDir, 'project-rules', 'claude');
|
|
422
|
+
rules.push(...claudeRules);
|
|
423
|
+
|
|
424
|
+
// Load GenCode rules second (higher priority)
|
|
425
|
+
const gencodeRulesDir = path.join(projectRoot, this.config.gencodeDir, this.config.rulesDir);
|
|
426
|
+
const gencodeRules = await this.loadRulesFromDir(gencodeRulesDir, 'project-rules', 'gencode');
|
|
427
|
+
rules.push(...gencodeRules);
|
|
428
|
+
|
|
429
|
+
return rules;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Load local memory files (both claude and gencode)
|
|
434
|
+
*/
|
|
435
|
+
private async loadLocalMemory(projectRoot: string): Promise<MemoryFile[]> {
|
|
436
|
+
const files: MemoryFile[] = [];
|
|
437
|
+
|
|
438
|
+
// Load Claude local files first
|
|
439
|
+
const claudeCandidates = [
|
|
440
|
+
path.join(projectRoot, this.config.claudeLocalFilename),
|
|
441
|
+
path.join(projectRoot, this.config.claudeDir, this.config.claudeLocalFilename),
|
|
442
|
+
];
|
|
443
|
+
|
|
444
|
+
for (const filePath of claudeCandidates) {
|
|
445
|
+
const file = await this.loadFile(filePath, 'local', 'claude');
|
|
446
|
+
if (file) {
|
|
447
|
+
files.push(file);
|
|
448
|
+
break;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Load GenCode local files second
|
|
453
|
+
const gencodeCandidates = [
|
|
454
|
+
path.join(projectRoot, this.config.gencodeLocalFilename),
|
|
455
|
+
path.join(projectRoot, this.config.gencodeDir, this.config.gencodeLocalFilename),
|
|
456
|
+
];
|
|
457
|
+
|
|
458
|
+
for (const filePath of gencodeCandidates) {
|
|
459
|
+
const file = await this.loadFile(filePath, 'local', 'gencode');
|
|
460
|
+
if (file) {
|
|
461
|
+
files.push(file);
|
|
462
|
+
break;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
return files;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Load rules from a directory
|
|
471
|
+
*/
|
|
472
|
+
private async loadRulesFromDir(
|
|
473
|
+
rulesDir: string,
|
|
474
|
+
level: 'user-rules' | 'project-rules',
|
|
475
|
+
namespace: MemoryNamespace
|
|
476
|
+
): Promise<MemoryRule[]> {
|
|
477
|
+
const rules: MemoryRule[] = [];
|
|
478
|
+
|
|
479
|
+
try {
|
|
480
|
+
const files = await glob('**/*.md', { cwd: rulesDir, absolute: true });
|
|
481
|
+
|
|
482
|
+
for (const filePath of files) {
|
|
483
|
+
try {
|
|
484
|
+
const stat = await fs.stat(filePath);
|
|
485
|
+
if (stat.size > this.config.maxFileSize) {
|
|
486
|
+
continue; // Skip files that are too large
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
490
|
+
const parsed = parseRuleFrontmatter(content);
|
|
491
|
+
|
|
492
|
+
rules.push({
|
|
493
|
+
path: filePath,
|
|
494
|
+
content: parsed.content,
|
|
495
|
+
patterns: parsed.paths,
|
|
496
|
+
isActive: false, // Will be set by activateRules
|
|
497
|
+
level,
|
|
498
|
+
namespace,
|
|
499
|
+
});
|
|
500
|
+
} catch {
|
|
501
|
+
// Skip invalid rule files
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
} catch {
|
|
505
|
+
// Rules directory doesn't exist
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
return rules;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Load a single file with import resolution
|
|
513
|
+
*/
|
|
514
|
+
private async loadFile(
|
|
515
|
+
filePath: string,
|
|
516
|
+
level: MemoryLevel,
|
|
517
|
+
namespace: MemoryNamespace
|
|
518
|
+
): Promise<MemoryFile | null> {
|
|
519
|
+
try {
|
|
520
|
+
const stat = await fs.stat(filePath);
|
|
521
|
+
|
|
522
|
+
if (stat.size > this.config.maxFileSize) {
|
|
523
|
+
return null;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
let content = await fs.readFile(filePath, 'utf-8');
|
|
527
|
+
const result = await this.importResolver.resolve(content, path.dirname(filePath));
|
|
528
|
+
content = result.content;
|
|
529
|
+
|
|
530
|
+
return {
|
|
531
|
+
path: filePath,
|
|
532
|
+
content,
|
|
533
|
+
level,
|
|
534
|
+
namespace,
|
|
535
|
+
loadedAt: new Date(),
|
|
536
|
+
resolvedImports: result.importedPaths,
|
|
537
|
+
};
|
|
538
|
+
} catch {
|
|
539
|
+
return null;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Find project root (git root or cwd)
|
|
545
|
+
*/
|
|
546
|
+
private async findProjectRoot(cwd: string): Promise<string> {
|
|
547
|
+
let current = cwd;
|
|
548
|
+
|
|
549
|
+
while (current !== '/') {
|
|
550
|
+
try {
|
|
551
|
+
await fs.access(path.join(current, '.git'));
|
|
552
|
+
return current;
|
|
553
|
+
} catch {
|
|
554
|
+
const parent = path.dirname(current);
|
|
555
|
+
if (parent === current) break;
|
|
556
|
+
current = parent;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
return cwd;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Quick add content to memory file
|
|
565
|
+
*/
|
|
566
|
+
async quickAdd(content: string, level: 'user' | 'project', cwd: string): Promise<string> {
|
|
567
|
+
let filePath: string;
|
|
568
|
+
const home = os.homedir();
|
|
569
|
+
|
|
570
|
+
if (level === 'user') {
|
|
571
|
+
const dir = path.join(home, this.config.gencodeDir);
|
|
572
|
+
await fs.mkdir(dir, { recursive: true });
|
|
573
|
+
filePath = path.join(dir, this.config.gencodeFilename);
|
|
574
|
+
} else {
|
|
575
|
+
const projectRoot = await this.findProjectRoot(cwd);
|
|
576
|
+
filePath = path.join(projectRoot, this.config.gencodeFilename);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// Read existing content
|
|
580
|
+
let existing = '';
|
|
581
|
+
try {
|
|
582
|
+
existing = await fs.readFile(filePath, 'utf-8');
|
|
583
|
+
} catch {
|
|
584
|
+
// File doesn't exist, create with header
|
|
585
|
+
existing = `# ${this.config.gencodeFilename.replace('.md', '')}\n\nThis file provides guidance when working with code in this repository.\n\n`;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// Append new content
|
|
589
|
+
const newContent = `${existing.trimEnd()}\n\n${content}\n`;
|
|
590
|
+
await fs.writeFile(filePath, newContent, 'utf-8');
|
|
591
|
+
|
|
592
|
+
return filePath;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Get list of loaded files for /memory command
|
|
597
|
+
*/
|
|
598
|
+
getLoadedFileList(): Array<{
|
|
599
|
+
path: string;
|
|
600
|
+
level: string;
|
|
601
|
+
namespace: string;
|
|
602
|
+
size: number;
|
|
603
|
+
type: 'file' | 'rule';
|
|
604
|
+
}> {
|
|
605
|
+
if (!this.loadedMemory) return [];
|
|
606
|
+
|
|
607
|
+
const list: Array<{
|
|
608
|
+
path: string;
|
|
609
|
+
level: string;
|
|
610
|
+
namespace: string;
|
|
611
|
+
size: number;
|
|
612
|
+
type: 'file' | 'rule';
|
|
613
|
+
}> = [];
|
|
614
|
+
|
|
615
|
+
for (const f of this.loadedMemory.files) {
|
|
616
|
+
list.push({
|
|
617
|
+
path: f.path,
|
|
618
|
+
level: f.level,
|
|
619
|
+
namespace: f.namespace,
|
|
620
|
+
size: f.content.length,
|
|
621
|
+
type: 'file',
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
for (const r of this.loadedMemory.rules) {
|
|
626
|
+
list.push({
|
|
627
|
+
path: r.path,
|
|
628
|
+
level: r.level,
|
|
629
|
+
namespace: r.namespace,
|
|
630
|
+
size: r.content.length,
|
|
631
|
+
type: 'rule',
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
return list;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* Get the path where /init would create a file
|
|
640
|
+
*/
|
|
641
|
+
getInitFilePath(cwd: string): string {
|
|
642
|
+
return path.join(cwd, this.config.gencodeFilename);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* Check if project memory already exists
|
|
647
|
+
*/
|
|
648
|
+
async hasProjectMemory(cwd: string): Promise<boolean> {
|
|
649
|
+
const projectRoot = await this.findProjectRoot(cwd);
|
|
650
|
+
const candidates = [
|
|
651
|
+
path.join(projectRoot, this.config.gencodeFilename),
|
|
652
|
+
path.join(projectRoot, this.config.gencodeDir, this.config.gencodeFilename),
|
|
653
|
+
path.join(projectRoot, this.config.claudeFilename),
|
|
654
|
+
path.join(projectRoot, this.config.claudeDir, this.config.claudeFilename),
|
|
655
|
+
];
|
|
656
|
+
|
|
657
|
+
for (const filePath of candidates) {
|
|
658
|
+
try {
|
|
659
|
+
await fs.access(filePath);
|
|
660
|
+
return true;
|
|
661
|
+
} catch {
|
|
662
|
+
continue;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
return false;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* Get the path of existing project memory file
|
|
671
|
+
*/
|
|
672
|
+
async getExistingProjectMemoryPath(cwd: string): Promise<string | null> {
|
|
673
|
+
const projectRoot = await this.findProjectRoot(cwd);
|
|
674
|
+
const candidates = [
|
|
675
|
+
path.join(projectRoot, this.config.gencodeFilename),
|
|
676
|
+
path.join(projectRoot, this.config.gencodeDir, this.config.gencodeFilename),
|
|
677
|
+
path.join(projectRoot, this.config.claudeFilename),
|
|
678
|
+
path.join(projectRoot, this.config.claudeDir, this.config.claudeFilename),
|
|
679
|
+
];
|
|
680
|
+
|
|
681
|
+
for (const filePath of candidates) {
|
|
682
|
+
try {
|
|
683
|
+
await fs.access(filePath);
|
|
684
|
+
return filePath;
|
|
685
|
+
} catch {
|
|
686
|
+
continue;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
return null;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* Get debug summary of loaded memory
|
|
695
|
+
*/
|
|
696
|
+
getDebugSummary(): string {
|
|
697
|
+
if (!this.loadedMemory) return 'Memory not loaded';
|
|
698
|
+
|
|
699
|
+
const lines: string[] = ['Memory Sources (in load order):'];
|
|
700
|
+
|
|
701
|
+
for (const source of this.loadedMemory.sources) {
|
|
702
|
+
const marker = source.level === 'enterprise' ? ' [enforced]' : '';
|
|
703
|
+
lines.push(` ${source.level}:${source.namespace} - ${source.path} (${source.size} bytes)${marker}`);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
if (this.loadedMemory.errors.length > 0) {
|
|
707
|
+
lines.push('');
|
|
708
|
+
lines.push('Errors:');
|
|
709
|
+
for (const error of this.loadedMemory.errors) {
|
|
710
|
+
lines.push(` - ${error}`);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
return lines.join('\n');
|
|
715
|
+
}
|
|
716
|
+
}
|