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.
Files changed (263) hide show
  1. package/.gencode/settings.local.json +7 -0
  2. package/README.md +11 -11
  3. package/dist/agent/agent.d.ts +42 -1
  4. package/dist/agent/agent.d.ts.map +1 -1
  5. package/dist/agent/agent.js +82 -15
  6. package/dist/agent/agent.js.map +1 -1
  7. package/dist/cli/components/App.d.ts +8 -1
  8. package/dist/cli/components/App.d.ts.map +1 -1
  9. package/dist/cli/components/App.js +231 -29
  10. package/dist/cli/components/App.js.map +1 -1
  11. package/dist/cli/components/CommandSuggestions.d.ts.map +1 -1
  12. package/dist/cli/components/CommandSuggestions.js +2 -0
  13. package/dist/cli/components/CommandSuggestions.js.map +1 -1
  14. package/dist/cli/components/Header.d.ts +1 -1
  15. package/dist/cli/components/Header.d.ts.map +1 -1
  16. package/dist/cli/components/Header.js +4 -6
  17. package/dist/cli/components/Header.js.map +1 -1
  18. package/dist/cli/components/Logo.d.ts +1 -0
  19. package/dist/cli/components/Logo.d.ts.map +1 -1
  20. package/dist/cli/components/Logo.js +16 -3
  21. package/dist/cli/components/Logo.js.map +1 -1
  22. package/dist/cli/components/Messages.d.ts +4 -4
  23. package/dist/cli/components/Messages.d.ts.map +1 -1
  24. package/dist/cli/components/Messages.js +51 -25
  25. package/dist/cli/components/Messages.js.map +1 -1
  26. package/dist/cli/components/PermissionPrompt.d.ts +60 -0
  27. package/dist/cli/components/PermissionPrompt.d.ts.map +1 -0
  28. package/dist/cli/components/PermissionPrompt.js +192 -0
  29. package/dist/cli/components/PermissionPrompt.js.map +1 -0
  30. package/dist/cli/components/ProviderManager.js +3 -3
  31. package/dist/cli/components/ProviderManager.js.map +1 -1
  32. package/dist/cli/components/Spinner.d.ts +7 -2
  33. package/dist/cli/components/Spinner.d.ts.map +1 -1
  34. package/dist/cli/components/Spinner.js +116 -25
  35. package/dist/cli/components/Spinner.js.map +1 -1
  36. package/dist/cli/components/TodoList.d.ts +7 -0
  37. package/dist/cli/components/TodoList.d.ts.map +1 -0
  38. package/dist/cli/components/TodoList.js +34 -0
  39. package/dist/cli/components/TodoList.js.map +1 -0
  40. package/dist/cli/components/index.d.ts +1 -0
  41. package/dist/cli/components/index.d.ts.map +1 -1
  42. package/dist/cli/components/index.js +1 -0
  43. package/dist/cli/components/index.js.map +1 -1
  44. package/dist/cli/index.js +47 -7
  45. package/dist/cli/index.js.map +1 -1
  46. package/dist/config/index.d.ts +13 -4
  47. package/dist/config/index.d.ts.map +1 -1
  48. package/dist/config/index.js +18 -3
  49. package/dist/config/index.js.map +1 -1
  50. package/dist/config/levels.d.ts +49 -0
  51. package/dist/config/levels.d.ts.map +1 -0
  52. package/dist/config/levels.js +222 -0
  53. package/dist/config/levels.js.map +1 -0
  54. package/dist/config/loader.d.ts +46 -0
  55. package/dist/config/loader.d.ts.map +1 -0
  56. package/dist/config/loader.js +153 -0
  57. package/dist/config/loader.js.map +1 -0
  58. package/dist/config/manager.d.ts +115 -15
  59. package/dist/config/manager.d.ts.map +1 -1
  60. package/dist/config/manager.js +260 -34
  61. package/dist/config/manager.js.map +1 -1
  62. package/dist/config/manager.test.d.ts +5 -0
  63. package/dist/config/manager.test.d.ts.map +1 -0
  64. package/dist/config/manager.test.js +192 -0
  65. package/dist/config/manager.test.js.map +1 -0
  66. package/dist/config/merger.d.ts +56 -0
  67. package/dist/config/merger.d.ts.map +1 -0
  68. package/dist/config/merger.js +177 -0
  69. package/dist/config/merger.js.map +1 -0
  70. package/dist/config/test-utils.d.ts +24 -0
  71. package/dist/config/test-utils.d.ts.map +1 -0
  72. package/dist/config/test-utils.js +55 -0
  73. package/dist/config/test-utils.js.map +1 -0
  74. package/dist/config/types.d.ts +78 -9
  75. package/dist/config/types.d.ts.map +1 -1
  76. package/dist/config/types.js +52 -2
  77. package/dist/config/types.js.map +1 -1
  78. package/dist/memory/import-resolver.d.ts +46 -0
  79. package/dist/memory/import-resolver.d.ts.map +1 -0
  80. package/dist/memory/import-resolver.js +117 -0
  81. package/dist/memory/import-resolver.js.map +1 -0
  82. package/dist/memory/index.d.ts +7 -6
  83. package/dist/memory/index.d.ts.map +1 -1
  84. package/dist/memory/index.js +7 -5
  85. package/dist/memory/index.js.map +1 -1
  86. package/dist/memory/init-prompt.d.ts +22 -0
  87. package/dist/memory/init-prompt.d.ts.map +1 -0
  88. package/dist/memory/init-prompt.js +103 -0
  89. package/dist/memory/init-prompt.js.map +1 -0
  90. package/dist/memory/memory-manager.d.ts +119 -0
  91. package/dist/memory/memory-manager.d.ts.map +1 -0
  92. package/dist/memory/memory-manager.js +587 -0
  93. package/dist/memory/memory-manager.js.map +1 -0
  94. package/dist/memory/rules-parser.d.ts +38 -0
  95. package/dist/memory/rules-parser.d.ts.map +1 -0
  96. package/dist/memory/rules-parser.js +69 -0
  97. package/dist/memory/rules-parser.js.map +1 -0
  98. package/dist/memory/test-utils.d.ts +20 -0
  99. package/dist/memory/test-utils.d.ts.map +1 -0
  100. package/dist/memory/test-utils.js +44 -0
  101. package/dist/memory/test-utils.js.map +1 -0
  102. package/dist/memory/types.d.ts +70 -63
  103. package/dist/memory/types.d.ts.map +1 -1
  104. package/dist/memory/types.js +42 -2
  105. package/dist/memory/types.js.map +1 -1
  106. package/dist/permissions/audit.d.ts +82 -0
  107. package/dist/permissions/audit.d.ts.map +1 -0
  108. package/dist/permissions/audit.js +229 -0
  109. package/dist/permissions/audit.js.map +1 -0
  110. package/dist/permissions/index.d.ts +11 -1
  111. package/dist/permissions/index.d.ts.map +1 -1
  112. package/dist/permissions/index.js +15 -0
  113. package/dist/permissions/index.js.map +1 -1
  114. package/dist/permissions/manager.d.ts +149 -13
  115. package/dist/permissions/manager.d.ts.map +1 -1
  116. package/dist/permissions/manager.js +480 -35
  117. package/dist/permissions/manager.js.map +1 -1
  118. package/dist/permissions/manager.test.d.ts +5 -0
  119. package/dist/permissions/manager.test.d.ts.map +1 -0
  120. package/dist/permissions/manager.test.js +213 -0
  121. package/dist/permissions/manager.test.js.map +1 -0
  122. package/dist/permissions/persistence.d.ts +74 -0
  123. package/dist/permissions/persistence.d.ts.map +1 -0
  124. package/dist/permissions/persistence.js +248 -0
  125. package/dist/permissions/persistence.js.map +1 -0
  126. package/dist/permissions/persistence.test.d.ts +5 -0
  127. package/dist/permissions/persistence.test.d.ts.map +1 -0
  128. package/dist/permissions/persistence.test.js +171 -0
  129. package/dist/permissions/persistence.test.js.map +1 -0
  130. package/dist/permissions/prompt-matcher.d.ts +64 -0
  131. package/dist/permissions/prompt-matcher.d.ts.map +1 -0
  132. package/dist/permissions/prompt-matcher.js +415 -0
  133. package/dist/permissions/prompt-matcher.js.map +1 -0
  134. package/dist/permissions/prompt-matcher.test.d.ts +5 -0
  135. package/dist/permissions/prompt-matcher.test.d.ts.map +1 -0
  136. package/dist/permissions/prompt-matcher.test.js +107 -0
  137. package/dist/permissions/prompt-matcher.test.js.map +1 -0
  138. package/dist/permissions/types.d.ts +157 -0
  139. package/dist/permissions/types.d.ts.map +1 -1
  140. package/dist/permissions/types.js +43 -8
  141. package/dist/permissions/types.js.map +1 -1
  142. package/dist/prompts/index.d.ts +92 -0
  143. package/dist/prompts/index.d.ts.map +1 -0
  144. package/dist/prompts/index.js +241 -0
  145. package/dist/prompts/index.js.map +1 -0
  146. package/dist/tools/builtin/bash.d.ts.map +1 -1
  147. package/dist/tools/builtin/bash.js +2 -1
  148. package/dist/tools/builtin/bash.js.map +1 -1
  149. package/dist/tools/builtin/edit.d.ts.map +1 -1
  150. package/dist/tools/builtin/edit.js +2 -1
  151. package/dist/tools/builtin/edit.js.map +1 -1
  152. package/dist/tools/builtin/glob.d.ts.map +1 -1
  153. package/dist/tools/builtin/glob.js +2 -1
  154. package/dist/tools/builtin/glob.js.map +1 -1
  155. package/dist/tools/builtin/grep.d.ts.map +1 -1
  156. package/dist/tools/builtin/grep.js +2 -1
  157. package/dist/tools/builtin/grep.js.map +1 -1
  158. package/dist/tools/builtin/read.d.ts.map +1 -1
  159. package/dist/tools/builtin/read.js +2 -1
  160. package/dist/tools/builtin/read.js.map +1 -1
  161. package/dist/tools/builtin/todowrite.d.ts +15 -0
  162. package/dist/tools/builtin/todowrite.d.ts.map +1 -0
  163. package/dist/tools/builtin/todowrite.js +88 -0
  164. package/dist/tools/builtin/todowrite.js.map +1 -0
  165. package/dist/tools/builtin/webfetch.d.ts.map +1 -1
  166. package/dist/tools/builtin/webfetch.js +2 -5
  167. package/dist/tools/builtin/webfetch.js.map +1 -1
  168. package/dist/tools/builtin/websearch.d.ts.map +1 -1
  169. package/dist/tools/builtin/websearch.js +2 -16
  170. package/dist/tools/builtin/websearch.js.map +1 -1
  171. package/dist/tools/builtin/write.d.ts.map +1 -1
  172. package/dist/tools/builtin/write.js +2 -1
  173. package/dist/tools/builtin/write.js.map +1 -1
  174. package/dist/tools/index.d.ts +7 -0
  175. package/dist/tools/index.d.ts.map +1 -1
  176. package/dist/tools/index.js +4 -0
  177. package/dist/tools/index.js.map +1 -1
  178. package/dist/tools/types.d.ts +22 -0
  179. package/dist/tools/types.d.ts.map +1 -1
  180. package/dist/tools/types.js +8 -0
  181. package/dist/tools/types.js.map +1 -1
  182. package/docs/config-system-comparison.md +707 -0
  183. package/docs/memory-system.md +238 -0
  184. package/docs/permissions.md +368 -0
  185. package/docs/proposals/0005-todo-system.md +350 -85
  186. package/docs/proposals/0006-memory-system.md +11 -10
  187. package/docs/proposals/0012-ask-user-question.md +941 -206
  188. package/docs/proposals/0023-permission-enhancements.md +61 -2
  189. package/docs/proposals/0041-configuration-system.md +33 -2
  190. package/docs/proposals/0042-prompt-optimization.md +866 -0
  191. package/docs/proposals/README.md +6 -5
  192. package/jest.config.js +26 -0
  193. package/package.json +8 -2
  194. package/src/agent/agent.ts +111 -16
  195. package/src/cli/components/App.tsx +309 -36
  196. package/src/cli/components/CommandSuggestions.tsx +2 -0
  197. package/src/cli/components/Header.tsx +11 -17
  198. package/src/cli/components/Logo.tsx +76 -9
  199. package/src/cli/components/Messages.tsx +73 -53
  200. package/src/cli/components/PermissionPrompt.tsx +388 -0
  201. package/src/cli/components/ProviderManager.tsx +5 -5
  202. package/src/cli/components/Spinner.tsx +138 -25
  203. package/src/cli/components/TodoList.tsx +54 -0
  204. package/src/cli/components/index.ts +6 -0
  205. package/src/cli/index.tsx +54 -6
  206. package/src/config/index.ts +78 -4
  207. package/src/config/levels.test.ts +163 -0
  208. package/src/config/levels.ts +285 -0
  209. package/src/config/loader.test.ts +120 -0
  210. package/src/config/loader.ts +178 -0
  211. package/src/config/manager.test.ts +215 -0
  212. package/src/config/manager.ts +328 -40
  213. package/src/config/merger.test.ts +360 -0
  214. package/src/config/merger.ts +221 -0
  215. package/src/config/test-utils.ts +79 -0
  216. package/src/config/types.ts +152 -9
  217. package/src/memory/import-resolver.test.ts +117 -0
  218. package/src/memory/import-resolver.ts +149 -0
  219. package/src/memory/index.ts +11 -0
  220. package/src/memory/init-prompt.ts +113 -0
  221. package/src/memory/memory-manager.test.ts +198 -0
  222. package/src/memory/memory-manager.ts +716 -0
  223. package/src/memory/rules-parser.test.ts +182 -0
  224. package/src/memory/rules-parser.ts +82 -0
  225. package/src/memory/test-utils.ts +60 -0
  226. package/src/memory/types.ts +119 -0
  227. package/src/permissions/audit.ts +284 -0
  228. package/src/permissions/index.ts +20 -1
  229. package/src/permissions/manager.test.ts +260 -0
  230. package/src/permissions/manager.ts +592 -40
  231. package/src/permissions/persistence.test.ts +220 -0
  232. package/src/permissions/persistence.ts +301 -0
  233. package/src/permissions/prompt-matcher.test.ts +213 -0
  234. package/src/permissions/prompt-matcher.ts +472 -0
  235. package/src/permissions/types.ts +236 -8
  236. package/src/prompts/index.test.ts +279 -0
  237. package/src/prompts/index.ts +306 -0
  238. package/src/prompts/system/anthropic.txt +29 -0
  239. package/src/prompts/system/base.txt +124 -0
  240. package/src/prompts/system/gemini.txt +35 -0
  241. package/src/prompts/system/generic.txt +128 -0
  242. package/src/prompts/system/openai.txt +29 -0
  243. package/src/prompts/tools/bash.txt +60 -0
  244. package/src/prompts/tools/edit.txt +29 -0
  245. package/src/prompts/tools/glob.txt +35 -0
  246. package/src/prompts/tools/grep.txt +43 -0
  247. package/src/prompts/tools/read.txt +22 -0
  248. package/src/prompts/tools/todowrite.txt +71 -0
  249. package/src/prompts/tools/webfetch.txt +34 -0
  250. package/src/prompts/tools/websearch.txt +41 -0
  251. package/src/prompts/tools/write.txt +23 -0
  252. package/src/tools/builtin/bash.ts +2 -1
  253. package/src/tools/builtin/edit.ts +2 -1
  254. package/src/tools/builtin/glob.ts +2 -1
  255. package/src/tools/builtin/grep.ts +2 -1
  256. package/src/tools/builtin/read.ts +2 -1
  257. package/src/tools/builtin/todowrite.ts +102 -0
  258. package/src/tools/builtin/webfetch.ts +2 -5
  259. package/src/tools/builtin/websearch.ts +2 -16
  260. package/src/tools/builtin/write.ts +2 -1
  261. package/src/tools/index.ts +4 -0
  262. package/src/tools/types.ts +12 -0
  263. 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
+ }