@ddse/acm-aicoder 0.5.0 → 0.5.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 (47) hide show
  1. package/dist/src/capability-map.d.ts +4 -0
  2. package/dist/src/capability-map.d.ts.map +1 -0
  3. package/dist/src/capability-map.js +289 -0
  4. package/dist/src/capability-map.js.map +1 -0
  5. package/dist/src/registries.d.ts +3 -0
  6. package/dist/src/registries.d.ts.map +1 -1
  7. package/dist/src/registries.js +11 -0
  8. package/dist/src/registries.js.map +1 -1
  9. package/dist/tsconfig.tsbuildinfo +1 -1
  10. package/package.json +21 -7
  11. package/.aicoder/index.json +0 -304
  12. package/AICODER_IMPLEMENTATION_PLAN_PHASE2.md +0 -284
  13. package/bin/interactive.tsx +0 -232
  14. package/docs/AICODER.png +0 -0
  15. package/docs/INTERACTIVE_CLI_GUIDE.md +0 -201
  16. package/docs/TUI_MOCKUP.md +0 -180
  17. package/src/config/providers.ts +0 -174
  18. package/src/config/session.ts +0 -143
  19. package/src/context/bm25.ts +0 -173
  20. package/src/context/code-search.ts +0 -188
  21. package/src/context/context-pack.ts +0 -133
  22. package/src/context/dependency-mapper.ts +0 -72
  23. package/src/context/index.ts +0 -8
  24. package/src/context/symbol-extractor.ts +0 -149
  25. package/src/context/test-mapper.ts +0 -77
  26. package/src/context/types.ts +0 -69
  27. package/src/context/workspace-indexer.ts +0 -249
  28. package/src/index.ts +0 -5
  29. package/src/registries.ts +0 -118
  30. package/src/runtime/budget-manager.ts +0 -118
  31. package/src/runtime/interactive-runtime.ts +0 -423
  32. package/src/tasks-v2/analysis-tasks.ts +0 -311
  33. package/src/tasks-v2/developer-tasks.ts +0 -437
  34. package/src/tasks-v2/index.ts +0 -3
  35. package/src/tools-v2/edit-tools.ts +0 -153
  36. package/src/tools-v2/index.ts +0 -6
  37. package/src/tools-v2/read-tools.ts +0 -286
  38. package/src/tools-v2/search-tools.ts +0 -175
  39. package/src/tools-v2/test-tools.ts +0 -147
  40. package/src/tools-v2/workspace-context.ts +0 -428
  41. package/src/ui/App.tsx +0 -392
  42. package/src/ui/components/ChatPane.tsx +0 -84
  43. package/src/ui/components/EventsPane.tsx +0 -81
  44. package/src/ui/components/GoalsTasksPane.tsx +0 -149
  45. package/src/ui/store.ts +0 -362
  46. package/tests/integration.test.ts +0 -537
  47. package/tsconfig.json +0 -22
@@ -1,249 +0,0 @@
1
- // Workspace Indexer - Fast file scanning and caching
2
- import * as fs from 'fs/promises';
3
- import * as path from 'path';
4
- import { createHash } from 'crypto';
5
- import type { FileMetadata, WorkspaceIndex } from './types.js';
6
-
7
- const DEFAULT_IGNORES = [
8
- 'node_modules',
9
- '.git',
10
- 'dist',
11
- 'build',
12
- '.next',
13
- '.cache',
14
- 'coverage',
15
- '.aicoder',
16
- '*.lock',
17
- 'package-lock.json',
18
- 'pnpm-lock.yaml',
19
- 'yarn.lock',
20
- ];
21
-
22
- const BINARY_EXTENSIONS = new Set([
23
- '.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico',
24
- '.pdf', '.zip', '.tar', '.gz', '.bz2',
25
- '.exe', '.dll', '.so', '.dylib',
26
- '.woff', '.woff2', '.ttf', '.eot',
27
- '.mp4', '.mp3', '.wav', '.avi',
28
- ]);
29
-
30
- const LANGUAGE_MAP: Record<string, string> = {
31
- '.ts': 'typescript',
32
- '.tsx': 'typescript',
33
- '.js': 'javascript',
34
- '.jsx': 'javascript',
35
- '.mjs': 'javascript',
36
- '.cjs': 'javascript',
37
- '.py': 'python',
38
- '.go': 'go',
39
- '.rs': 'rust',
40
- '.java': 'java',
41
- '.c': 'c',
42
- '.cpp': 'cpp',
43
- '.h': 'c',
44
- '.hpp': 'cpp',
45
- '.md': 'markdown',
46
- '.json': 'json',
47
- '.yaml': 'yaml',
48
- '.yml': 'yaml',
49
- '.toml': 'toml',
50
- '.xml': 'xml',
51
- '.html': 'html',
52
- '.css': 'css',
53
- '.scss': 'scss',
54
- '.sql': 'sql',
55
- '.sh': 'shell',
56
- };
57
-
58
- export class WorkspaceIndexer {
59
- private rootPath: string;
60
- private ignorePatterns: Set<string>;
61
- private cacheDir: string;
62
-
63
- constructor(rootPath: string, additionalIgnores: string[] = []) {
64
- this.rootPath = path.resolve(rootPath);
65
- this.ignorePatterns = new Set([...DEFAULT_IGNORES, ...additionalIgnores]);
66
- this.cacheDir = path.join(this.rootPath, '.aicoder');
67
- }
68
-
69
- /**
70
- * Build or refresh the workspace index
71
- */
72
- async buildIndex(options: { useCache?: boolean; maxFileSize?: number } = {}): Promise<WorkspaceIndex> {
73
- const useCache = options.useCache ?? true;
74
- const maxFileSize = options.maxFileSize ?? 1_000_000; // 1MB default
75
-
76
- // Try to load from cache
77
- if (useCache) {
78
- const cached = await this.loadCache();
79
- if (cached) {
80
- return cached;
81
- }
82
- }
83
-
84
- // Scan workspace
85
- const files: FileMetadata[] = [];
86
- await this.scanDirectory(this.rootPath, files, maxFileSize);
87
-
88
- const index: WorkspaceIndex = {
89
- files,
90
- totalFiles: files.length,
91
- totalSize: files.reduce((sum, f) => sum + f.size, 0),
92
- indexedAt: new Date().toISOString(),
93
- rootPath: this.rootPath,
94
- };
95
-
96
- // Save to cache
97
- if (useCache) {
98
- await this.saveCache(index);
99
- }
100
-
101
- return index;
102
- }
103
-
104
- /**
105
- * Recursively scan directory for files
106
- */
107
- private async scanDirectory(
108
- dirPath: string,
109
- files: FileMetadata[],
110
- maxFileSize: number
111
- ): Promise<void> {
112
- try {
113
- const entries = await fs.readdir(dirPath, { withFileTypes: true });
114
-
115
- for (const entry of entries) {
116
- const fullPath = path.join(dirPath, entry.name);
117
- const relativePath = path.relative(this.rootPath, fullPath);
118
-
119
- // Skip ignored patterns
120
- if (this.shouldIgnore(relativePath, entry.name)) {
121
- continue;
122
- }
123
-
124
- if (entry.isDirectory()) {
125
- await this.scanDirectory(fullPath, files, maxFileSize);
126
- } else if (entry.isFile()) {
127
- try {
128
- const stats = await fs.stat(fullPath);
129
-
130
- // Skip files that are too large
131
- if (stats.size > maxFileSize) {
132
- continue;
133
- }
134
-
135
- const ext = path.extname(entry.name);
136
- const isBinary = BINARY_EXTENSIONS.has(ext);
137
-
138
- // Compute hash for non-binary files
139
- let hash = '';
140
- if (!isBinary && stats.size < 100_000) { // Hash only small text files
141
- try {
142
- const content = await fs.readFile(fullPath, 'utf-8');
143
- hash = createHash('sha256').update(content).digest('hex').substring(0, 16);
144
- } catch {
145
- // Skip files that can't be read
146
- continue;
147
- }
148
- }
149
-
150
- files.push({
151
- path: relativePath,
152
- size: stats.size,
153
- mtime: stats.mtimeMs,
154
- hash,
155
- language: LANGUAGE_MAP[ext] || 'unknown',
156
- isBinary,
157
- });
158
- } catch (error) {
159
- // Skip files we can't stat
160
- continue;
161
- }
162
- }
163
- }
164
- } catch (error) {
165
- // Skip directories we can't read
166
- }
167
- }
168
-
169
- /**
170
- * Check if path should be ignored
171
- */
172
- private shouldIgnore(relativePath: string, name: string): boolean {
173
- // Check exact matches
174
- if (this.ignorePatterns.has(name)) {
175
- return true;
176
- }
177
-
178
- // Check if any part of the path matches ignore patterns
179
- const parts = relativePath.split(path.sep);
180
- for (const part of parts) {
181
- if (this.ignorePatterns.has(part)) {
182
- return true;
183
- }
184
- }
185
-
186
- // Check wildcard patterns
187
- for (const pattern of this.ignorePatterns) {
188
- if (pattern.includes('*')) {
189
- const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$');
190
- if (regex.test(name)) {
191
- return true;
192
- }
193
- }
194
- }
195
-
196
- return false;
197
- }
198
-
199
- /**
200
- * Load index from cache
201
- */
202
- private async loadCache(): Promise<WorkspaceIndex | null> {
203
- try {
204
- const cachePath = path.join(this.cacheDir, 'index.json');
205
- const content = await fs.readFile(cachePath, 'utf-8');
206
- const index: WorkspaceIndex = JSON.parse(content);
207
-
208
- // Validate cache is recent (within 1 hour)
209
- const cacheAge = Date.now() - new Date(index.indexedAt).getTime();
210
- if (cacheAge > 3600_000) {
211
- return null;
212
- }
213
-
214
- return index;
215
- } catch {
216
- return null;
217
- }
218
- }
219
-
220
- /**
221
- * Save index to cache
222
- */
223
- private async saveCache(index: WorkspaceIndex): Promise<void> {
224
- try {
225
- await fs.mkdir(this.cacheDir, { recursive: true });
226
- const cachePath = path.join(this.cacheDir, 'index.json');
227
- await fs.writeFile(cachePath, JSON.stringify(index, null, 2), 'utf-8');
228
- } catch {
229
- // Cache save is best-effort
230
- }
231
- }
232
-
233
- /**
234
- * Get files by language
235
- */
236
- static filterByLanguage(index: WorkspaceIndex, languages: string[]): FileMetadata[] {
237
- const langSet = new Set(languages);
238
- return index.files.filter(f => langSet.has(f.language));
239
- }
240
-
241
- /**
242
- * Get recently modified files
243
- */
244
- static getRecentFiles(index: WorkspaceIndex, count: number = 10): FileMetadata[] {
245
- return [...index.files]
246
- .sort((a, b) => b.mtime - a.mtime)
247
- .slice(0, count);
248
- }
249
- }
package/src/index.ts DELETED
@@ -1,5 +0,0 @@
1
- // ACM AI Coder - Phase 2 exports
2
- export * from './tools-v2/index.js';
3
- export * from './tasks-v2/index.js';
4
- export * from './registries.js';
5
- export * from './context/index.js';
package/src/registries.ts DELETED
@@ -1,118 +0,0 @@
1
- // Registries and Policy for AI Coder
2
- import {
3
- CapabilityRegistry,
4
- ToolRegistry,
5
- PolicyEngine,
6
- type Capability,
7
- type Tool,
8
- type Task,
9
- type PolicyDecision,
10
- } from '@ddse/acm-sdk';
11
-
12
- /**
13
- * Simple Capability Registry
14
- */
15
- export class SimpleCapabilityRegistry extends CapabilityRegistry {
16
- private tasks = new Map<string, Task>();
17
- private capabilities = new Map<string, Capability>();
18
-
19
- register(capability: Capability, task: Task): void {
20
- this.capabilities.set(capability.name, capability);
21
- this.tasks.set(capability.name, task);
22
- }
23
-
24
- list(): Capability[] {
25
- return Array.from(this.capabilities.values());
26
- }
27
-
28
- has(name: string): boolean {
29
- return this.capabilities.has(name);
30
- }
31
-
32
- resolve(name: string): Task | undefined {
33
- return this.tasks.get(name);
34
- }
35
-
36
- inputSchema(name: string): unknown | undefined {
37
- return this.capabilities.get(name)?.inputSchema;
38
- }
39
-
40
- outputSchema(name: string): unknown | undefined {
41
- return this.capabilities.get(name)?.outputSchema;
42
- }
43
- }
44
-
45
- /**
46
- * Simple Tool Registry
47
- */
48
- export class SimpleToolRegistry extends ToolRegistry {
49
- private tools = new Map<string, Tool>();
50
-
51
- register(tool: Tool): void {
52
- this.tools.set(tool.name(), tool);
53
- }
54
-
55
- get(name: string): Tool | undefined {
56
- return this.tools.get(name);
57
- }
58
-
59
- list(): string[] {
60
- return Array.from(this.tools.keys());
61
- }
62
- }
63
-
64
- /**
65
- * Simple Policy Engine with safety rules for code modifications
66
- */
67
- export class SimplePolicyEngine implements PolicyEngine {
68
- private allowedPaths: string[] = [];
69
- private blockedActions: string[] = [];
70
-
71
- setAllowedPaths(paths: string[]): void {
72
- this.allowedPaths = paths;
73
- }
74
-
75
- setBlockedActions(actions: string[]): void {
76
- this.blockedActions = actions;
77
- }
78
-
79
- async evaluate(
80
- action: 'plan.admit' | 'task.pre' | 'task.post',
81
- payload: any
82
- ): Promise<PolicyDecision> {
83
- // Check if action is blocked
84
- if (action === 'task.pre' && this.blockedActions.includes(payload?.action)) {
85
- return {
86
- allow: false,
87
- reason: `Action ${payload?.action} is blocked by policy`,
88
- };
89
- }
90
-
91
- // For code editing operations, check path is allowed
92
- if (action === 'task.pre' && (payload?.action === 'fix_bug' || payload?.action === 'implement_feature')) {
93
- const targetPath = payload?.path || payload?.targetPath;
94
-
95
- if (this.allowedPaths.length > 0 && targetPath && !this.allowedPaths.includes('*')) {
96
- const isAllowed = this.allowedPaths.some(allowed =>
97
- targetPath.includes(allowed)
98
- );
99
-
100
- if (!isAllowed) {
101
- return {
102
- allow: false,
103
- reason: `Path ${targetPath} is not in allowed paths`,
104
- };
105
- }
106
- }
107
- }
108
-
109
- // Default: allow
110
- return {
111
- allow: true,
112
- limits: {
113
- timeoutMs: 30000,
114
- retries: 3,
115
- },
116
- };
117
- }
118
- }
@@ -1,118 +0,0 @@
1
- // Token budget manager
2
- // Tracks cumulative token usage and ensures calls stay within provider limits
3
-
4
- import {
5
- ProviderMetadata,
6
- getProviderMetadata,
7
- estimateTokenCount,
8
- } from '../config/providers.js';
9
-
10
- export interface BudgetEstimate {
11
- inputTokens: number;
12
- outputTokens: number;
13
- totalTokens: number;
14
- }
15
-
16
- export interface BudgetStatus {
17
- totalInputTokens: number;
18
- totalOutputTokens: number;
19
- totalTokens: number;
20
- maxTokens?: number;
21
- remainingTokens?: number;
22
- callCount: number;
23
- }
24
-
25
- export class BudgetManager {
26
- private metadata: ProviderMetadata;
27
- private maxTokens?: number;
28
- private totalInputTokens = 0;
29
- private totalOutputTokens = 0;
30
- private callCount = 0;
31
-
32
- constructor(model: string, overrideMaxTokens?: number) {
33
- this.metadata = getProviderMetadata(model);
34
- this.maxTokens = overrideMaxTokens ?? this.metadata.maxContextTokens;
35
- }
36
-
37
- /**
38
- * Estimate whether an upcoming inference fits within the token allowance
39
- * @param inputText Planned prompt text
40
- * @param estimatedOutputTokens Rough guess for completion tokens
41
- */
42
- checkBudget(inputText: string, estimatedOutputTokens: number = 1000): BudgetEstimate {
43
- const inputTokens = estimateTokenCount(inputText);
44
- const totalTokens = inputTokens + estimatedOutputTokens;
45
-
46
- if (this.maxTokens !== undefined) {
47
- const projected = this.totalTokensConsumed() + totalTokens;
48
- if (projected > this.maxTokens) {
49
- throw new TokenBudgetExceededError(
50
- `Token allowance exceeded: projected ${projected} tokens > limit ${this.maxTokens}`,
51
- {
52
- inputTokens,
53
- outputTokens: estimatedOutputTokens,
54
- totalTokens,
55
- },
56
- this.getStatus()
57
- );
58
- }
59
- }
60
-
61
- return {
62
- inputTokens,
63
- outputTokens: estimatedOutputTokens,
64
- totalTokens,
65
- };
66
- }
67
-
68
- /**
69
- * Record actual token usage after a call completes
70
- */
71
- recordUsage(actualInputTokens: number, actualOutputTokens: number): void {
72
- this.totalInputTokens += actualInputTokens;
73
- this.totalOutputTokens += actualOutputTokens;
74
- this.callCount += 1;
75
- }
76
-
77
- getStatus(): BudgetStatus {
78
- const totalTokens = this.totalTokensConsumed();
79
- const status: BudgetStatus = {
80
- totalInputTokens: this.totalInputTokens,
81
- totalOutputTokens: this.totalOutputTokens,
82
- totalTokens,
83
- maxTokens: this.maxTokens,
84
- callCount: this.callCount,
85
- };
86
-
87
- if (this.maxTokens !== undefined) {
88
- status.remainingTokens = Math.max(0, this.maxTokens - totalTokens);
89
- }
90
-
91
- return status;
92
- }
93
-
94
- getMetadata(): ProviderMetadata {
95
- return this.metadata;
96
- }
97
-
98
- reset(): void {
99
- this.totalInputTokens = 0;
100
- this.totalOutputTokens = 0;
101
- this.callCount = 0;
102
- }
103
-
104
- private totalTokensConsumed(): number {
105
- return this.totalInputTokens + this.totalOutputTokens;
106
- }
107
- }
108
-
109
- export class TokenBudgetExceededError extends Error {
110
- constructor(
111
- message: string,
112
- public estimate: BudgetEstimate,
113
- public status: BudgetStatus
114
- ) {
115
- super(message);
116
- this.name = 'TokenBudgetExceededError';
117
- }
118
- }