thatgfsj-code 0.2.1 → 0.2.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 (64) hide show
  1. package/CHANGELOG.md +131 -0
  2. package/DEVELOPMENT.md +286 -0
  3. package/README.md +131 -30
  4. package/dist/core/ai-engine.d.ts +17 -0
  5. package/dist/core/ai-engine.d.ts.map +1 -1
  6. package/dist/core/ai-engine.js +23 -7
  7. package/dist/core/ai-engine.js.map +1 -1
  8. package/dist/index.js +18 -27
  9. package/dist/index.js.map +1 -1
  10. package/dist/repl/input.d.ts +8 -0
  11. package/dist/repl/input.d.ts.map +1 -1
  12. package/dist/repl/input.js +12 -0
  13. package/dist/repl/input.js.map +1 -1
  14. package/dist/repl/loop.d.ts +56 -0
  15. package/dist/repl/loop.d.ts.map +1 -1
  16. package/dist/repl/loop.js +333 -4
  17. package/dist/repl/loop.js.map +1 -1
  18. package/dist/repl/output.d.ts.map +1 -1
  19. package/dist/repl/output.js +3 -1
  20. package/dist/repl/output.js.map +1 -1
  21. package/dist/repl/welcome.js +1 -1
  22. package/dist/repl/welcome.js.map +1 -1
  23. package/docs/API_KEY_GUIDE.md +6 -0
  24. package/docs/FAQ.md +25 -3
  25. package/package.json +35 -3
  26. package/.patches/0001-fix-repl-replace-readline-with-inquirer-input-for-ke.patch +0 -279
  27. package/.patches/0002-fix-repl-stream-AI-output-directly-to-stdout-so-term.patch +0 -564
  28. package/.patches/0003-fix-session-break-the-hallucination-loop-in-assistan.patch +0 -194
  29. package/.patches/0004-chore-release-bump-version-to-0.2.1.patch +0 -24
  30. package/ROADMAP.md +0 -107
  31. package/src/agent/core.ts +0 -179
  32. package/src/agent/index.ts +0 -8
  33. package/src/agent/intent.ts +0 -181
  34. package/src/agent/streaming.ts +0 -132
  35. package/src/core/ai-engine.ts +0 -437
  36. package/src/core/cli.ts +0 -171
  37. package/src/core/config.ts +0 -147
  38. package/src/core/context-compactor.ts +0 -245
  39. package/src/core/hooks.ts +0 -196
  40. package/src/core/permissions.ts +0 -308
  41. package/src/core/session.ts +0 -165
  42. package/src/core/skills.ts +0 -208
  43. package/src/core/state.ts +0 -120
  44. package/src/core/subagent.ts +0 -195
  45. package/src/core/system-prompt.ts +0 -163
  46. package/src/core/tool-registry.ts +0 -157
  47. package/src/core/types.ts +0 -280
  48. package/src/index.ts +0 -544
  49. package/src/mcp/client.ts +0 -330
  50. package/src/repl/index.ts +0 -8
  51. package/src/repl/input.ts +0 -139
  52. package/src/repl/loop.ts +0 -280
  53. package/src/repl/output.ts +0 -222
  54. package/src/repl/welcome.ts +0 -296
  55. package/src/tools/file.ts +0 -117
  56. package/src/tools/git.ts +0 -132
  57. package/src/tools/index.ts +0 -48
  58. package/src/tools/search.ts +0 -263
  59. package/src/tools/shell.ts +0 -181
  60. package/src/utils/diff-preview.ts +0 -202
  61. package/src/utils/index.ts +0 -8
  62. package/src/utils/memory.ts +0 -223
  63. package/src/utils/project-context.ts +0 -207
  64. package/tsconfig.json +0 -19
@@ -1,223 +0,0 @@
1
- /**
2
- * Session Memory - Remember modifications during conversation
3
- */
4
-
5
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
6
- import { join, dirname } from 'path';
7
- import { homedir } from 'os';
8
-
9
- export interface MemoryEntry {
10
- id: string;
11
- timestamp: number;
12
- type: 'file_read' | 'file_write' | 'file_edit' | 'command' | 'git' | 'note';
13
- description: string;
14
- details: Record<string, any>;
15
- }
16
-
17
- export class SessionMemory {
18
- private entries: MemoryEntry[] = [];
19
- private sessionId: string;
20
- private memoryFile?: string;
21
-
22
- constructor(sessionId?: string) {
23
- this.sessionId = sessionId || this.generateId();
24
- }
25
-
26
- /**
27
- * Add an entry to memory
28
- */
29
- add(entry: Omit<MemoryEntry, 'id' | 'timestamp'>): void {
30
- this.entries.push({
31
- ...entry,
32
- id: this.generateId(),
33
- timestamp: Date.now()
34
- });
35
- }
36
-
37
- /**
38
- * Record file read
39
- */
40
- recordRead(path: string, lines?: number): void {
41
- this.add({
42
- type: 'file_read',
43
- description: `Read ${path}`,
44
- details: { path, lines }
45
- });
46
- }
47
-
48
- /**
49
- * Record file write
50
- */
51
- recordWrite(path: string, content?: string): void {
52
- this.add({
53
- type: 'file_write',
54
- description: `Wrote ${path}`,
55
- details: { path, size: content?.length || 0 }
56
- });
57
- }
58
-
59
- /**
60
- * Record file edit
61
- */
62
- recordEdit(path: string, operation: string): void {
63
- this.add({
64
- type: 'file_edit',
65
- description: `Edited ${path}: ${operation}`,
66
- details: { path, operation }
67
- });
68
- }
69
-
70
- /**
71
- * Record command execution
72
- */
73
- recordCommand(command: string, output?: string): void {
74
- this.add({
75
- type: 'command',
76
- description: `Executed: ${command}`,
77
- details: { command, output: output?.slice(0, 200) }
78
- });
79
- }
80
-
81
- /**
82
- * Record git operation
83
- */
84
- recordGit(operation: string, result: string): void {
85
- this.add({
86
- type: 'git',
87
- description: `Git ${operation}`,
88
- details: { operation, result: result.slice(0, 200) }
89
- });
90
- }
91
-
92
- /**
93
- * Record a note
94
- */
95
- recordNote(note: string): void {
96
- this.add({
97
- type: 'note',
98
- description: note,
99
- details: {}
100
- });
101
- }
102
-
103
- /**
104
- * Get all entries
105
- */
106
- getEntries(): MemoryEntry[] {
107
- return [...this.entries];
108
- }
109
-
110
- /**
111
- * Get recent entries
112
- */
113
- getRecent(count: number = 10): MemoryEntry[] {
114
- return this.entries.slice(-count);
115
- }
116
-
117
- /**
118
- * Get entries by type
119
- */
120
- getByType(type: MemoryEntry['type']): MemoryEntry[] {
121
- return this.entries.filter(e => e.type === type);
122
- }
123
-
124
- /**
125
- * Search entries
126
- */
127
- search(query: string): MemoryEntry[] {
128
- const lower = query.toLowerCase();
129
- return this.entries.filter(e =>
130
- e.description.toLowerCase().includes(lower) ||
131
- JSON.stringify(e.details).toLowerCase().includes(lower)
132
- );
133
- }
134
-
135
- /**
136
- * Get summary
137
- */
138
- getSummary(): string {
139
- if (this.entries.length === 0) {
140
- return 'No actions recorded yet.';
141
- }
142
-
143
- const types = this.entries.reduce((acc, e) => {
144
- acc[e.type] = (acc[e.type] || 0) + 1;
145
- return acc;
146
- }, {} as Record<string, number>);
147
-
148
- const lines = ['📋 Session Summary:'];
149
-
150
- for (const [type, count] of Object.entries(types)) {
151
- const emoji = {
152
- 'file_read': '📖',
153
- 'file_write': '✍️',
154
- 'file_edit': '📝',
155
- 'command': '⚡',
156
- 'git': '📊',
157
- 'note': '📌'
158
- }[type] || '•';
159
-
160
- lines.push(` ${emoji} ${type}: ${count}`);
161
- }
162
-
163
- return lines.join('\n');
164
- }
165
-
166
- /**
167
- * Export to file
168
- */
169
- save(): void {
170
- if (!this.memoryFile) {
171
- const homeDir = homedir();
172
- const memDir = join(homeDir, '.thatgfsj', 'memory');
173
-
174
- if (!existsSync(memDir)) {
175
- mkdirSync(memDir, { recursive: true });
176
- }
177
-
178
- this.memoryFile = join(memDir, `${this.sessionId}.json`);
179
- }
180
-
181
- writeFileSync(this.memoryFile, JSON.stringify({
182
- sessionId: this.sessionId,
183
- entries: this.entries,
184
- savedAt: Date.now()
185
- }, null, 2));
186
- }
187
-
188
- /**
189
- * Load from file
190
- */
191
- load(sessionId: string): boolean {
192
- const homeDir = homedir();
193
- const memoryFile = join(homeDir, '.thatgfsj', 'memory', `${sessionId}.json`);
194
-
195
- if (!existsSync(memoryFile)) {
196
- return false;
197
- }
198
-
199
- try {
200
- const data = JSON.parse(readFileSync(memoryFile, 'utf-8'));
201
- this.entries = data.entries || [];
202
- this.sessionId = data.sessionId;
203
- this.memoryFile = memoryFile;
204
- return true;
205
- } catch {
206
- return false;
207
- }
208
- }
209
-
210
- /**
211
- * Clear memory
212
- */
213
- clear(): void {
214
- this.entries = [];
215
- }
216
-
217
- /**
218
- * Generate unique ID
219
- */
220
- private generateId(): string {
221
- return `${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
222
- }
223
- }
@@ -1,207 +0,0 @@
1
- /**
2
- * Project Context - Auto-detect and read project files
3
- */
4
-
5
- import { readFileSync, existsSync, readdirSync, statSync } from 'fs';
6
- import { join, relative } from 'path';
7
-
8
- export interface ProjectInfo {
9
- root: string;
10
- name: string;
11
- language: string;
12
- buildTool?: string;
13
- gitIgnore: string[];
14
- files: string[];
15
- hasGit: boolean;
16
- packageJson?: PackageJson;
17
- }
18
-
19
- export interface PackageJson {
20
- name: string;
21
- version: string;
22
- description?: string;
23
- scripts?: Record<string, string>;
24
- dependencies?: Record<string, string>;
25
- devDependencies?: Record<string, string>;
26
- }
27
-
28
- export class ProjectContext {
29
- private root: string;
30
-
31
- constructor(root: string = process.cwd()) {
32
- this.root = root;
33
- }
34
-
35
- /**
36
- * Detect project type and gather context
37
- */
38
- async detect(): Promise<ProjectInfo> {
39
- const info: ProjectInfo = {
40
- root: this.root,
41
- name: this.getProjectName(),
42
- language: this.detectLanguage(),
43
- buildTool: this.detectBuildTool(),
44
- gitIgnore: this.readGitIgnore(),
45
- files: this.listFiles(),
46
- hasGit: existsSync(join(this.root, '.git'))
47
- };
48
-
49
- const pkg = this.readPackageJson();
50
- if (pkg) {
51
- info.packageJson = pkg;
52
- }
53
-
54
- return info;
55
- }
56
-
57
- /**
58
- * Get project name
59
- */
60
- private getProjectName(): string {
61
- const pkg = this.readPackageJson();
62
- if (pkg?.name) {
63
- return pkg.name;
64
- }
65
- return this.root.split(/[\\/]/).pop() || 'unknown';
66
- }
67
-
68
- /**
69
- * Detect primary language
70
- */
71
- private detectLanguage(): string {
72
- const langCounts: Record<string, number> = {};
73
-
74
- const scan = (dir: string, depth: number = 0) => {
75
- if (depth > 3) return;
76
- try {
77
- const items = readdirSync(dir);
78
- for (const item of items) {
79
- if (['node_modules', '.git', 'dist'].includes(item)) continue;
80
- const fullPath = join(dir, item);
81
- try {
82
- const stat = statSync(fullPath);
83
- if (stat.isDirectory()) {
84
- scan(fullPath, depth + 1);
85
- } else if (stat.isFile()) {
86
- const ext = item.split('.').pop()?.toLowerCase();
87
- if (ext) langCounts[ext] = (langCounts[ext] || 0) + 1;
88
- }
89
- } catch { /* skip */ }
90
- }
91
- } catch { /* skip */ }
92
- };
93
-
94
- scan(this.root);
95
-
96
- const extToLang: Record<string, string> = {
97
- 'ts': 'TypeScript', 'tsx': 'TypeScript',
98
- 'js': 'JavaScript', 'jsx': 'JavaScript', 'mjs': 'JavaScript',
99
- 'py': 'Python', 'java': 'Java', 'go': 'Go', 'rs': 'Rust',
100
- 'c': 'C/C++', 'cpp': 'C/C++', 'h': 'C/C++', 'hpp': 'C/C++',
101
- 'cs': 'C#', 'rb': 'Ruby', 'php': 'PHP',
102
- };
103
-
104
- for (const [ext, count] of Object.entries(langCounts)) {
105
- const lang = extToLang[ext];
106
- if (lang && count > 0) return lang;
107
- }
108
-
109
- return 'Unknown';
110
- }
111
-
112
- /**
113
- * Detect build tool
114
- */
115
- private detectBuildTool(): string | undefined {
116
- if (existsSync(join(this.root, 'package.json'))) return 'npm/yarn/pnpm';
117
- if (existsSync(join(this.root, 'Cargo.toml'))) return 'Cargo';
118
- if (existsSync(join(this.root, 'go.mod'))) return 'Go';
119
- if (existsSync(join(this.root, 'pom.xml'))) return 'Maven';
120
- if (existsSync(join(this.root, 'build.gradle'))) return 'Gradle';
121
- if (existsSync(join(this.root, 'Makefile'))) return 'Make';
122
- return undefined;
123
- }
124
-
125
- /**
126
- * Read .gitignore
127
- */
128
- private readGitIgnore(): string[] {
129
- const path = join(this.root, '.gitignore');
130
- if (!existsSync(path)) return [];
131
- try {
132
- return readFileSync(path, 'utf-8').split('\n')
133
- .map(l => l.trim()).filter(l => l && !l.startsWith('#'));
134
- } catch { return []; }
135
- }
136
-
137
- /**
138
- * Read package.json
139
- */
140
- private readPackageJson(): PackageJson | null {
141
- const path = join(this.root, 'package.json');
142
- if (!existsSync(path)) return null;
143
- try {
144
- return JSON.parse(readFileSync(path, 'utf-8'));
145
- } catch { return null; }
146
- }
147
-
148
- /**
149
- * List project files
150
- */
151
- private listFiles(maxFiles: number = 100): string[] {
152
- const files: string[] = [];
153
- const gitIgnore = this.readGitIgnore();
154
-
155
- const scan = (dir: string, depth: number = 0) => {
156
- if (depth > 4 || files.length >= maxFiles) return;
157
- try {
158
- const items = readdirSync(dir);
159
- for (const item of items) {
160
- if (files.length >= maxFiles) break;
161
- if (this.isIgnored(item, gitIgnore)) continue;
162
- const fullPath = join(dir, item);
163
- const relPath = relative(this.root, fullPath);
164
- try {
165
- const stat = statSync(fullPath);
166
- if (stat.isDirectory()) {
167
- files.push(relPath + '/');
168
- scan(fullPath, depth + 1);
169
- } else if (stat.isFile()) {
170
- files.push(relPath);
171
- }
172
- } catch { /* skip */ }
173
- }
174
- } catch { /* skip */ }
175
- };
176
-
177
- scan(this.root);
178
- return files;
179
- }
180
-
181
- /**
182
- * Check if ignored
183
- */
184
- private isIgnored(name: string, gitIgnore: string[]): boolean {
185
- const common = ['node_modules', '.git', 'dist', 'build', '__pycache__', '.env'];
186
- if (common.includes(name)) return true;
187
- return gitIgnore.includes(name);
188
- }
189
-
190
- /**
191
- * Get context summary
192
- */
193
- async getContextSummary(): Promise<string> {
194
- const info = await this.detect();
195
- const lines = [`📁 Project: ${info.name}`, `💻 Language: ${info.language}`];
196
- if (info.buildTool) lines.push(`🔧 Build: ${info.buildTool}`);
197
- if (info.hasGit) lines.push(`📊 Git: Yes`);
198
- if (info.packageJson) {
199
- lines.push(`📦 Package: ${info.packageJson.name}@${info.packageJson.version}`);
200
- if (info.packageJson.scripts) {
201
- lines.push(` Scripts: ${Object.keys(info.packageJson.scripts).slice(0, 5).join(', ')}`);
202
- }
203
- }
204
- lines.push(`📁 Files: ${info.files.length} files scanned`);
205
- return lines.join('\n');
206
- }
207
- }
package/tsconfig.json DELETED
@@ -1,19 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "ESNext",
5
- "moduleResolution": "node",
6
- "outDir": "./dist",
7
- "rootDir": "./src",
8
- "strict": true,
9
- "esModuleInterop": true,
10
- "skipLibCheck": true,
11
- "forceConsistentCasingInFileNames": true,
12
- "resolveJsonModule": true,
13
- "declaration": true,
14
- "declarationMap": true,
15
- "sourceMap": true
16
- },
17
- "include": ["src/**/*"],
18
- "exclude": ["node_modules", "dist"]
19
- }