@ddse/acm-aicoder 0.5.0 → 0.5.1

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 (43) 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/tsconfig.tsbuildinfo +1 -1
  6. package/package.json +21 -7
  7. package/.aicoder/index.json +0 -304
  8. package/AICODER_IMPLEMENTATION_PLAN_PHASE2.md +0 -284
  9. package/bin/interactive.tsx +0 -232
  10. package/docs/AICODER.png +0 -0
  11. package/docs/INTERACTIVE_CLI_GUIDE.md +0 -201
  12. package/docs/TUI_MOCKUP.md +0 -180
  13. package/src/config/providers.ts +0 -174
  14. package/src/config/session.ts +0 -143
  15. package/src/context/bm25.ts +0 -173
  16. package/src/context/code-search.ts +0 -188
  17. package/src/context/context-pack.ts +0 -133
  18. package/src/context/dependency-mapper.ts +0 -72
  19. package/src/context/index.ts +0 -8
  20. package/src/context/symbol-extractor.ts +0 -149
  21. package/src/context/test-mapper.ts +0 -77
  22. package/src/context/types.ts +0 -69
  23. package/src/context/workspace-indexer.ts +0 -249
  24. package/src/index.ts +0 -5
  25. package/src/registries.ts +0 -118
  26. package/src/runtime/budget-manager.ts +0 -118
  27. package/src/runtime/interactive-runtime.ts +0 -423
  28. package/src/tasks-v2/analysis-tasks.ts +0 -311
  29. package/src/tasks-v2/developer-tasks.ts +0 -437
  30. package/src/tasks-v2/index.ts +0 -3
  31. package/src/tools-v2/edit-tools.ts +0 -153
  32. package/src/tools-v2/index.ts +0 -6
  33. package/src/tools-v2/read-tools.ts +0 -286
  34. package/src/tools-v2/search-tools.ts +0 -175
  35. package/src/tools-v2/test-tools.ts +0 -147
  36. package/src/tools-v2/workspace-context.ts +0 -428
  37. package/src/ui/App.tsx +0 -392
  38. package/src/ui/components/ChatPane.tsx +0 -84
  39. package/src/ui/components/EventsPane.tsx +0 -81
  40. package/src/ui/components/GoalsTasksPane.tsx +0 -149
  41. package/src/ui/store.ts +0 -362
  42. package/tests/integration.test.ts +0 -537
  43. 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
- }