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,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rules Parser Tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { parseRuleFrontmatter, matchesPatterns } from './rules-parser.js';
|
|
6
|
+
|
|
7
|
+
describe('Rules Parser', () => {
|
|
8
|
+
describe('parseRuleFrontmatter', () => {
|
|
9
|
+
it('should parse simple YAML frontmatter with paths', () => {
|
|
10
|
+
const content = `---
|
|
11
|
+
paths:
|
|
12
|
+
- "src/**/*.ts"
|
|
13
|
+
- "lib/**/*.ts"
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# Rule Content
|
|
17
|
+
|
|
18
|
+
This is the rule body.`;
|
|
19
|
+
|
|
20
|
+
const result = parseRuleFrontmatter(content);
|
|
21
|
+
|
|
22
|
+
expect(result.paths).toEqual(['src/**/*.ts', 'lib/**/*.ts']);
|
|
23
|
+
expect(result.content).toBe('# Rule Content\n\nThis is the rule body.');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should return empty paths for content without frontmatter', () => {
|
|
27
|
+
const content = `# Rule Without Frontmatter
|
|
28
|
+
|
|
29
|
+
Just regular markdown content.`;
|
|
30
|
+
|
|
31
|
+
const result = parseRuleFrontmatter(content);
|
|
32
|
+
|
|
33
|
+
expect(result.paths).toEqual([]);
|
|
34
|
+
expect(result.content).toBe('# Rule Without Frontmatter\n\nJust regular markdown content.');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should return empty paths when frontmatter has no paths field', () => {
|
|
38
|
+
const content = `---
|
|
39
|
+
title: Some Rule
|
|
40
|
+
description: A rule without path scoping
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
# Rule Content`;
|
|
44
|
+
|
|
45
|
+
const result = parseRuleFrontmatter(content);
|
|
46
|
+
|
|
47
|
+
expect(result.paths).toEqual([]);
|
|
48
|
+
expect(result.content).toBe('# Rule Content');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should handle single path as array', () => {
|
|
52
|
+
const content = `---
|
|
53
|
+
paths:
|
|
54
|
+
- "src/api/**/*.ts"
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
# API Rules`;
|
|
58
|
+
|
|
59
|
+
const result = parseRuleFrontmatter(content);
|
|
60
|
+
|
|
61
|
+
expect(result.paths).toEqual(['src/api/**/*.ts']);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should filter non-string paths', () => {
|
|
65
|
+
const content = `---
|
|
66
|
+
paths:
|
|
67
|
+
- "valid/path/*.ts"
|
|
68
|
+
- 123
|
|
69
|
+
- true
|
|
70
|
+
- "another/valid/*.js"
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
# Mixed Paths`;
|
|
74
|
+
|
|
75
|
+
const result = parseRuleFrontmatter(content);
|
|
76
|
+
|
|
77
|
+
expect(result.paths).toEqual(['valid/path/*.ts', 'another/valid/*.js']);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should handle empty frontmatter', () => {
|
|
81
|
+
const content = `---
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
# Empty Frontmatter`;
|
|
85
|
+
|
|
86
|
+
const result = parseRuleFrontmatter(content);
|
|
87
|
+
|
|
88
|
+
expect(result.paths).toEqual([]);
|
|
89
|
+
expect(result.content).toBe('# Empty Frontmatter');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should trim content whitespace', () => {
|
|
93
|
+
const content = `---
|
|
94
|
+
paths: []
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
# Content with extra whitespace
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
`;
|
|
102
|
+
|
|
103
|
+
const result = parseRuleFrontmatter(content);
|
|
104
|
+
|
|
105
|
+
expect(result.content).toBe('# Content with extra whitespace');
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
describe('matchesPatterns', () => {
|
|
110
|
+
it('should return true when patterns is empty (always active)', () => {
|
|
111
|
+
expect(matchesPatterns('any/file.ts', [])).toBe(true);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should match glob patterns', () => {
|
|
115
|
+
const patterns = ['src/**/*.ts'];
|
|
116
|
+
|
|
117
|
+
expect(matchesPatterns('src/components/Button.ts', patterns)).toBe(true);
|
|
118
|
+
expect(matchesPatterns('src/deep/nested/file.ts', patterns)).toBe(true);
|
|
119
|
+
expect(matchesPatterns('lib/file.ts', patterns)).toBe(false);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should match multiple patterns (OR logic)', () => {
|
|
123
|
+
const patterns = ['src/**/*.ts', 'lib/**/*.ts'];
|
|
124
|
+
|
|
125
|
+
expect(matchesPatterns('src/file.ts', patterns)).toBe(true);
|
|
126
|
+
expect(matchesPatterns('lib/file.ts', patterns)).toBe(true);
|
|
127
|
+
expect(matchesPatterns('test/file.ts', patterns)).toBe(false);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should match file extensions', () => {
|
|
131
|
+
const patterns = ['**/*.test.ts', '**/*.spec.ts'];
|
|
132
|
+
|
|
133
|
+
expect(matchesPatterns('src/Button.test.ts', patterns)).toBe(true);
|
|
134
|
+
expect(matchesPatterns('lib/utils.spec.ts', patterns)).toBe(true);
|
|
135
|
+
expect(matchesPatterns('src/Button.ts', patterns)).toBe(false);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should handle matchBase for simple patterns', () => {
|
|
139
|
+
const patterns = ['*.ts'];
|
|
140
|
+
|
|
141
|
+
// With matchBase: true, *.ts should match file.ts anywhere
|
|
142
|
+
expect(matchesPatterns('file.ts', patterns)).toBe(true);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should match specific directories', () => {
|
|
146
|
+
const patterns = ['src/api/**', 'src/routes/**'];
|
|
147
|
+
|
|
148
|
+
expect(matchesPatterns('src/api/users.ts', patterns)).toBe(true);
|
|
149
|
+
expect(matchesPatterns('src/routes/index.ts', patterns)).toBe(true);
|
|
150
|
+
expect(matchesPatterns('src/components/Button.ts', patterns)).toBe(false);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should handle dot files when dot option is true', () => {
|
|
154
|
+
const patterns = ['**/*.md'];
|
|
155
|
+
|
|
156
|
+
// Should match hidden directories
|
|
157
|
+
expect(matchesPatterns('.github/README.md', patterns)).toBe(true);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('should handle negation in paths', () => {
|
|
161
|
+
// This tests behavior - minimatch handles negation differently
|
|
162
|
+
const patterns = ['src/**/*.ts'];
|
|
163
|
+
|
|
164
|
+
expect(matchesPatterns('src/index.ts', patterns)).toBe(true);
|
|
165
|
+
expect(matchesPatterns('src/index.js', patterns)).toBe(false);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('should handle complex patterns', () => {
|
|
169
|
+
const patterns = ['src/{api,routes}/**/*.ts'];
|
|
170
|
+
|
|
171
|
+
expect(matchesPatterns('src/api/users.ts', patterns)).toBe(true);
|
|
172
|
+
expect(matchesPatterns('src/routes/auth.ts', patterns)).toBe(true);
|
|
173
|
+
expect(matchesPatterns('src/utils/helpers.ts', patterns)).toBe(false);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('should match relative paths', () => {
|
|
177
|
+
const patterns = ['./src/**/*.ts'];
|
|
178
|
+
|
|
179
|
+
expect(matchesPatterns('./src/file.ts', patterns)).toBe(true);
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rules Parser - Parse frontmatter from rules files
|
|
3
|
+
*
|
|
4
|
+
* Uses gray-matter for YAML frontmatter parsing.
|
|
5
|
+
* Supports 'paths:' frontmatter for glob-based rule scoping.
|
|
6
|
+
*
|
|
7
|
+
* Example rule file:
|
|
8
|
+
* ---
|
|
9
|
+
* paths:
|
|
10
|
+
* - "src/api/**\/*.ts"
|
|
11
|
+
* - "src/routes/**\/*.ts"
|
|
12
|
+
* ---
|
|
13
|
+
*
|
|
14
|
+
* # API Development Rules
|
|
15
|
+
* - Use async/await consistently
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import matter from 'gray-matter';
|
|
19
|
+
import { minimatch } from 'minimatch';
|
|
20
|
+
import type { MemoryRule } from './types.js';
|
|
21
|
+
|
|
22
|
+
export interface ParsedRule {
|
|
23
|
+
paths: string[];
|
|
24
|
+
content: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Parse frontmatter from a rule file
|
|
29
|
+
*/
|
|
30
|
+
export function parseRuleFrontmatter(fileContent: string): ParsedRule {
|
|
31
|
+
try {
|
|
32
|
+
const { data, content } = matter(fileContent);
|
|
33
|
+
|
|
34
|
+
// Extract paths from frontmatter
|
|
35
|
+
let paths: string[] = [];
|
|
36
|
+
if (data.paths) {
|
|
37
|
+
if (Array.isArray(data.paths)) {
|
|
38
|
+
paths = data.paths.filter((p: unknown) => typeof p === 'string');
|
|
39
|
+
} else if (typeof data.paths === 'string') {
|
|
40
|
+
paths = [data.paths];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return { paths, content: content.trim() };
|
|
45
|
+
} catch {
|
|
46
|
+
// If frontmatter parsing fails, return full content with no paths
|
|
47
|
+
return { paths: [], content: fileContent };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Check if a file path matches any of the glob patterns
|
|
53
|
+
*/
|
|
54
|
+
export function matchesPatterns(filePath: string, patterns: string[]): boolean {
|
|
55
|
+
if (patterns.length === 0) {
|
|
56
|
+
return true; // No patterns = always active
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return patterns.some((pattern) =>
|
|
60
|
+
minimatch(filePath, pattern, {
|
|
61
|
+
matchBase: true,
|
|
62
|
+
dot: true,
|
|
63
|
+
})
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Activate rules based on current file context
|
|
69
|
+
*/
|
|
70
|
+
export function activateRules(rules: MemoryRule[], currentFile?: string): MemoryRule[] {
|
|
71
|
+
return rules.map((rule) => ({
|
|
72
|
+
...rule,
|
|
73
|
+
isActive: currentFile ? matchesPatterns(currentFile, rule.patterns) : rule.patterns.length === 0,
|
|
74
|
+
}));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Get only active rules
|
|
79
|
+
*/
|
|
80
|
+
export function getActiveRules(rules: MemoryRule[]): MemoryRule[] {
|
|
81
|
+
return rules.filter((rule) => rule.isActive);
|
|
82
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared test utilities for memory tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as fs from 'fs/promises';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import * as os from 'os';
|
|
8
|
+
|
|
9
|
+
export interface TestProject {
|
|
10
|
+
tempDir: string;
|
|
11
|
+
projectDir: string;
|
|
12
|
+
cleanup: () => Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Create a test project with temp directory and git marker
|
|
17
|
+
*/
|
|
18
|
+
export async function createTestProject(prefix = 'gencode-test-'): Promise<TestProject> {
|
|
19
|
+
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), prefix));
|
|
20
|
+
const projectDir = path.join(tempDir, 'project');
|
|
21
|
+
|
|
22
|
+
await fs.mkdir(projectDir, { recursive: true });
|
|
23
|
+
await fs.mkdir(path.join(projectDir, '.git'));
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
tempDir,
|
|
27
|
+
projectDir,
|
|
28
|
+
cleanup: async () => {
|
|
29
|
+
await fs.rm(tempDir, { recursive: true, force: true });
|
|
30
|
+
delete process.env.GENCODE_CONFIG_DIRS;
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Write memory file to a directory
|
|
37
|
+
*/
|
|
38
|
+
export async function writeMemory(
|
|
39
|
+
projectDir: string,
|
|
40
|
+
namespace: 'claude' | 'gencode',
|
|
41
|
+
content: string,
|
|
42
|
+
options: { local?: boolean; inDir?: boolean } = {}
|
|
43
|
+
): Promise<string> {
|
|
44
|
+
const { local = false, inDir = true } = options;
|
|
45
|
+
const filename = namespace === 'claude'
|
|
46
|
+
? (local ? 'CLAUDE.local.md' : 'CLAUDE.md')
|
|
47
|
+
: (local ? 'AGENT.local.md' : 'AGENT.md');
|
|
48
|
+
|
|
49
|
+
let filePath: string;
|
|
50
|
+
if (inDir) {
|
|
51
|
+
const dir = path.join(projectDir, namespace === 'claude' ? '.claude' : '.gencode');
|
|
52
|
+
await fs.mkdir(dir, { recursive: true });
|
|
53
|
+
filePath = path.join(dir, filename);
|
|
54
|
+
} else {
|
|
55
|
+
filePath = path.join(projectDir, filename);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
await fs.writeFile(filePath, content);
|
|
59
|
+
return filePath;
|
|
60
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory System Types
|
|
3
|
+
*
|
|
4
|
+
* Hierarchical memory loading compatible with Claude Code:
|
|
5
|
+
* At each level, both .gencode and .claude are loaded and merged (gencode has higher priority).
|
|
6
|
+
*
|
|
7
|
+
* Levels:
|
|
8
|
+
* - Enterprise: System-wide managed memory (enforced)
|
|
9
|
+
* - User: ~/.gencode/ + ~/.claude/ (both loaded, gencode content appears later)
|
|
10
|
+
* - User Rules: ~/.gencode/rules/ + ~/.claude/rules/
|
|
11
|
+
* - Extra: GENCODE_CONFIG_DIRS directories
|
|
12
|
+
* - Project: ./AGENT.md, .gencode/, ./CLAUDE.md, .claude/ (recursive upward search)
|
|
13
|
+
* - Project Rules: .gencode/rules/ + .claude/rules/
|
|
14
|
+
* - Local: *.local.md files (gitignored)
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
export type MemoryLevel =
|
|
18
|
+
| 'enterprise'
|
|
19
|
+
| 'user'
|
|
20
|
+
| 'user-rules'
|
|
21
|
+
| 'extra'
|
|
22
|
+
| 'project'
|
|
23
|
+
| 'project-rules'
|
|
24
|
+
| 'local';
|
|
25
|
+
|
|
26
|
+
export type MemoryNamespace = 'gencode' | 'claude' | 'extra';
|
|
27
|
+
|
|
28
|
+
export interface MemoryFile {
|
|
29
|
+
path: string;
|
|
30
|
+
content: string;
|
|
31
|
+
level: MemoryLevel;
|
|
32
|
+
namespace: MemoryNamespace;
|
|
33
|
+
loadedAt: Date;
|
|
34
|
+
resolvedImports: string[];
|
|
35
|
+
enforced?: boolean; // For enterprise level
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface MemoryRule {
|
|
39
|
+
path: string;
|
|
40
|
+
content: string;
|
|
41
|
+
patterns: string[]; // Glob patterns from 'paths:' frontmatter
|
|
42
|
+
isActive: boolean; // Whether current file context matches
|
|
43
|
+
level: 'user-rules' | 'project-rules';
|
|
44
|
+
namespace: MemoryNamespace;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface MemoryConfig {
|
|
48
|
+
// GenCode file names (higher priority)
|
|
49
|
+
gencodeFilename: string;
|
|
50
|
+
gencodeLocalFilename: string;
|
|
51
|
+
gencodeDir: string;
|
|
52
|
+
|
|
53
|
+
// Claude file names (lower priority, loaded first)
|
|
54
|
+
claudeFilename: string;
|
|
55
|
+
claudeLocalFilename: string;
|
|
56
|
+
claudeDir: string;
|
|
57
|
+
|
|
58
|
+
// Common settings
|
|
59
|
+
rulesDir: string;
|
|
60
|
+
maxFileSize: number;
|
|
61
|
+
maxTotalSize: number;
|
|
62
|
+
maxImportDepth: number;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const DEFAULT_MEMORY_CONFIG: MemoryConfig = {
|
|
66
|
+
// GenCode
|
|
67
|
+
gencodeFilename: 'AGENT.md',
|
|
68
|
+
gencodeLocalFilename: 'AGENT.local.md',
|
|
69
|
+
gencodeDir: '.gencode',
|
|
70
|
+
|
|
71
|
+
// Claude
|
|
72
|
+
claudeFilename: 'CLAUDE.md',
|
|
73
|
+
claudeLocalFilename: 'CLAUDE.local.md',
|
|
74
|
+
claudeDir: '.claude',
|
|
75
|
+
|
|
76
|
+
// Common
|
|
77
|
+
rulesDir: 'rules',
|
|
78
|
+
maxFileSize: 100 * 1024, // 100KB
|
|
79
|
+
maxTotalSize: 500 * 1024, // 500KB
|
|
80
|
+
maxImportDepth: 5,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// Legacy compatibility
|
|
84
|
+
export const LEGACY_MEMORY_CONFIG = {
|
|
85
|
+
primaryFilename: 'AGENT.md',
|
|
86
|
+
fallbackFilename: 'CLAUDE.md',
|
|
87
|
+
localFilename: 'AGENT.local.md',
|
|
88
|
+
localFallbackFilename: 'CLAUDE.local.md',
|
|
89
|
+
primaryUserDir: '.gencode',
|
|
90
|
+
fallbackUserDir: '.claude',
|
|
91
|
+
primaryLocalDir: '.gencode',
|
|
92
|
+
fallbackLocalDir: '.claude',
|
|
93
|
+
rulesDir: 'rules',
|
|
94
|
+
maxFileSize: 100 * 1024,
|
|
95
|
+
maxTotalSize: 500 * 1024,
|
|
96
|
+
maxImportDepth: 5,
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export interface LoadedMemory {
|
|
100
|
+
files: MemoryFile[];
|
|
101
|
+
rules: MemoryRule[];
|
|
102
|
+
totalSize: number;
|
|
103
|
+
context: string;
|
|
104
|
+
errors: string[];
|
|
105
|
+
sources: MemorySource[]; // For debugging
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface MemorySource {
|
|
109
|
+
level: MemoryLevel;
|
|
110
|
+
namespace: MemoryNamespace;
|
|
111
|
+
path: string;
|
|
112
|
+
type: 'file' | 'rule';
|
|
113
|
+
size: number;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface MemoryLoadOptions {
|
|
117
|
+
cwd: string;
|
|
118
|
+
currentFile?: string; // For activating path-scoped rules
|
|
119
|
+
}
|