neuronlayer 0.1.0
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/CONTRIBUTING.md +127 -0
- package/LICENSE +21 -0
- package/README.md +305 -0
- package/dist/index.js +38016 -0
- package/esbuild.config.js +26 -0
- package/package.json +63 -0
- package/src/cli/commands.ts +382 -0
- package/src/core/adr-exporter.ts +253 -0
- package/src/core/architecture/architecture-enforcement.ts +228 -0
- package/src/core/architecture/duplicate-detector.ts +288 -0
- package/src/core/architecture/index.ts +6 -0
- package/src/core/architecture/pattern-learner.ts +306 -0
- package/src/core/architecture/pattern-library.ts +403 -0
- package/src/core/architecture/pattern-validator.ts +324 -0
- package/src/core/change-intelligence/bug-correlator.ts +444 -0
- package/src/core/change-intelligence/change-intelligence.ts +221 -0
- package/src/core/change-intelligence/change-tracker.ts +334 -0
- package/src/core/change-intelligence/fix-suggester.ts +340 -0
- package/src/core/change-intelligence/index.ts +5 -0
- package/src/core/code-verifier.ts +843 -0
- package/src/core/confidence/confidence-scorer.ts +251 -0
- package/src/core/confidence/conflict-checker.ts +289 -0
- package/src/core/confidence/index.ts +5 -0
- package/src/core/confidence/source-tracker.ts +263 -0
- package/src/core/confidence/warning-detector.ts +241 -0
- package/src/core/context-rot/compaction.ts +284 -0
- package/src/core/context-rot/context-health.ts +243 -0
- package/src/core/context-rot/context-rot-prevention.ts +213 -0
- package/src/core/context-rot/critical-context.ts +221 -0
- package/src/core/context-rot/drift-detector.ts +255 -0
- package/src/core/context-rot/index.ts +7 -0
- package/src/core/context.ts +263 -0
- package/src/core/decision-extractor.ts +339 -0
- package/src/core/decisions.ts +69 -0
- package/src/core/deja-vu.ts +421 -0
- package/src/core/engine.ts +1455 -0
- package/src/core/feature-context.ts +726 -0
- package/src/core/ghost-mode.ts +412 -0
- package/src/core/learning.ts +485 -0
- package/src/core/living-docs/activity-tracker.ts +296 -0
- package/src/core/living-docs/architecture-generator.ts +428 -0
- package/src/core/living-docs/changelog-generator.ts +348 -0
- package/src/core/living-docs/component-generator.ts +230 -0
- package/src/core/living-docs/doc-engine.ts +110 -0
- package/src/core/living-docs/doc-validator.ts +282 -0
- package/src/core/living-docs/index.ts +8 -0
- package/src/core/project-manager.ts +297 -0
- package/src/core/summarizer.ts +267 -0
- package/src/core/test-awareness/change-validator.ts +499 -0
- package/src/core/test-awareness/index.ts +5 -0
- package/src/index.ts +49 -0
- package/src/indexing/ast.ts +563 -0
- package/src/indexing/embeddings.ts +85 -0
- package/src/indexing/indexer.ts +245 -0
- package/src/indexing/watcher.ts +78 -0
- package/src/server/gateways/aggregator.ts +374 -0
- package/src/server/gateways/index.ts +473 -0
- package/src/server/gateways/memory-ghost.ts +343 -0
- package/src/server/gateways/memory-query.ts +452 -0
- package/src/server/gateways/memory-record.ts +346 -0
- package/src/server/gateways/memory-review.ts +410 -0
- package/src/server/gateways/memory-status.ts +517 -0
- package/src/server/gateways/memory-verify.ts +392 -0
- package/src/server/gateways/router.ts +434 -0
- package/src/server/gateways/types.ts +610 -0
- package/src/server/mcp.ts +154 -0
- package/src/server/resources.ts +85 -0
- package/src/server/tools.ts +2261 -0
- package/src/storage/database.ts +262 -0
- package/src/storage/tier1.ts +135 -0
- package/src/storage/tier2.ts +764 -0
- package/src/storage/tier3.ts +123 -0
- package/src/types/documentation.ts +619 -0
- package/src/types/index.ts +222 -0
- package/src/utils/config.ts +193 -0
- package/src/utils/files.ts +117 -0
- package/src/utils/time.ts +37 -0
- package/src/utils/tokens.ts +52 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
// Core types for MemoryLayer
|
|
2
|
+
|
|
3
|
+
export interface Position {
|
|
4
|
+
line: number;
|
|
5
|
+
column: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface ActiveFile {
|
|
9
|
+
path: string;
|
|
10
|
+
content: string;
|
|
11
|
+
cursorPosition?: Position;
|
|
12
|
+
language: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface Session {
|
|
16
|
+
startTime: Date;
|
|
17
|
+
filesViewed: string[];
|
|
18
|
+
currentGoal?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ContextSnippet {
|
|
22
|
+
file: string;
|
|
23
|
+
content: string;
|
|
24
|
+
timestamp: Date;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface Tier1Context {
|
|
28
|
+
activeFile: ActiveFile | null;
|
|
29
|
+
recentDecisions: Decision[];
|
|
30
|
+
session: Session;
|
|
31
|
+
immediateContext: ContextSnippet[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface Decision {
|
|
35
|
+
id: string;
|
|
36
|
+
title: string;
|
|
37
|
+
description: string;
|
|
38
|
+
files: string[];
|
|
39
|
+
tags: string[];
|
|
40
|
+
createdAt: Date;
|
|
41
|
+
// Phase 4: Team features
|
|
42
|
+
author?: string;
|
|
43
|
+
status?: 'proposed' | 'accepted' | 'deprecated' | 'superseded';
|
|
44
|
+
supersededBy?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface SearchResult {
|
|
48
|
+
file: string;
|
|
49
|
+
preview: string;
|
|
50
|
+
similarity: number;
|
|
51
|
+
lineStart: number;
|
|
52
|
+
lineEnd: number;
|
|
53
|
+
lastModified: number;
|
|
54
|
+
score?: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface FileMetadata {
|
|
58
|
+
id: number;
|
|
59
|
+
path: string;
|
|
60
|
+
contentHash: string;
|
|
61
|
+
preview: string;
|
|
62
|
+
language: string;
|
|
63
|
+
sizeBytes: number;
|
|
64
|
+
lastModified: number;
|
|
65
|
+
indexedAt: number;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface AssemblyOptions {
|
|
69
|
+
maxTokens?: number;
|
|
70
|
+
currentFile?: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface AssembledContext {
|
|
74
|
+
context: string;
|
|
75
|
+
sources: string[];
|
|
76
|
+
tokenCount: number;
|
|
77
|
+
decisions: Decision[];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface ProjectSummary {
|
|
81
|
+
name: string;
|
|
82
|
+
description: string;
|
|
83
|
+
languages: string[];
|
|
84
|
+
totalFiles: number;
|
|
85
|
+
totalLines: number;
|
|
86
|
+
keyDirectories: string[];
|
|
87
|
+
recentDecisions: Decision[];
|
|
88
|
+
dependencies: string[];
|
|
89
|
+
architectureNotes: string;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface ContextParts {
|
|
93
|
+
working: {
|
|
94
|
+
activeFile: ActiveFile | null;
|
|
95
|
+
};
|
|
96
|
+
relevant: SearchResult[];
|
|
97
|
+
archive: string[];
|
|
98
|
+
decisions: Decision[];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface TokenBudgetAllocation {
|
|
102
|
+
tier1: number;
|
|
103
|
+
tier2: number;
|
|
104
|
+
tier3: number;
|
|
105
|
+
decisions: number;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface DependencyRelation {
|
|
109
|
+
sourceFileId: number;
|
|
110
|
+
targetFileId: number;
|
|
111
|
+
relationship: 'imports' | 'exports' | 'calls' | 'type_reference';
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface IndexingProgress {
|
|
115
|
+
total: number;
|
|
116
|
+
indexed: number;
|
|
117
|
+
current?: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface MemoryLayerConfig {
|
|
121
|
+
projectPath: string;
|
|
122
|
+
dataDir: string;
|
|
123
|
+
maxTokens: number;
|
|
124
|
+
embeddingModel: string;
|
|
125
|
+
watchIgnore: string[];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Phase 2: AST & Symbol types
|
|
129
|
+
|
|
130
|
+
export type SymbolKind = 'function' | 'class' | 'interface' | 'type' | 'variable' | 'method' | 'property' | 'enum' | 'constant';
|
|
131
|
+
|
|
132
|
+
export interface CodeSymbol {
|
|
133
|
+
id?: number;
|
|
134
|
+
fileId: number;
|
|
135
|
+
filePath: string;
|
|
136
|
+
kind: SymbolKind;
|
|
137
|
+
name: string;
|
|
138
|
+
signature?: string;
|
|
139
|
+
docstring?: string;
|
|
140
|
+
lineStart: number;
|
|
141
|
+
lineEnd: number;
|
|
142
|
+
exported: boolean;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export interface Import {
|
|
146
|
+
fileId: number;
|
|
147
|
+
filePath: string;
|
|
148
|
+
importedFrom: string; // The module/file being imported
|
|
149
|
+
importedSymbols: string[]; // What's being imported (* for all)
|
|
150
|
+
isDefault: boolean;
|
|
151
|
+
isNamespace: boolean;
|
|
152
|
+
lineNumber: number;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export interface Export {
|
|
156
|
+
fileId: number;
|
|
157
|
+
filePath: string;
|
|
158
|
+
exportedName: string;
|
|
159
|
+
localName?: string; // If renamed: export { foo as bar }
|
|
160
|
+
isDefault: boolean;
|
|
161
|
+
lineNumber: number;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export interface FileDependency {
|
|
165
|
+
sourceFile: string;
|
|
166
|
+
targetFile: string;
|
|
167
|
+
imports: string[]; // What symbols are imported
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export interface SymbolSearchResult {
|
|
171
|
+
symbol: CodeSymbol;
|
|
172
|
+
file: string;
|
|
173
|
+
preview: string;
|
|
174
|
+
relevance: number;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Phase 5: Active Feature Context types
|
|
178
|
+
|
|
179
|
+
export interface FeatureFile {
|
|
180
|
+
path: string;
|
|
181
|
+
lastTouched: Date;
|
|
182
|
+
touchCount: number;
|
|
183
|
+
recentLines: number[];
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export interface FeatureChange {
|
|
187
|
+
file: string;
|
|
188
|
+
timestamp: Date;
|
|
189
|
+
diff: string;
|
|
190
|
+
linesChanged: number[];
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export interface FeatureQuery {
|
|
194
|
+
query: string;
|
|
195
|
+
timestamp: Date;
|
|
196
|
+
filesUsed: string[];
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export interface ActiveFeatureContext {
|
|
200
|
+
id: string;
|
|
201
|
+
name: string;
|
|
202
|
+
files: FeatureFile[];
|
|
203
|
+
changes: FeatureChange[];
|
|
204
|
+
queries: FeatureQuery[];
|
|
205
|
+
startedAt: Date;
|
|
206
|
+
lastActiveAt: Date;
|
|
207
|
+
status: 'active' | 'paused' | 'completed';
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export interface HotContext {
|
|
211
|
+
files: Array<{
|
|
212
|
+
path: string;
|
|
213
|
+
content: string | null;
|
|
214
|
+
touchCount: number;
|
|
215
|
+
}>;
|
|
216
|
+
changes: FeatureChange[];
|
|
217
|
+
queries: FeatureQuery[];
|
|
218
|
+
summary: string;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Re-export documentation types
|
|
222
|
+
export * from './documentation.js';
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { join, resolve } from 'path';
|
|
2
|
+
import type { MemoryLayerConfig } from '../types/index.js';
|
|
3
|
+
|
|
4
|
+
export function getDefaultConfig(projectPath: string): MemoryLayerConfig {
|
|
5
|
+
const normalizedPath = resolve(projectPath);
|
|
6
|
+
|
|
7
|
+
return {
|
|
8
|
+
projectPath: normalizedPath,
|
|
9
|
+
// Store in project directory (standard practice like .git/, .vscode/)
|
|
10
|
+
dataDir: join(normalizedPath, '.memorylayer'),
|
|
11
|
+
maxTokens: 6000,
|
|
12
|
+
embeddingModel: 'Xenova/all-MiniLM-L6-v2', // Fallback model, faster and smaller
|
|
13
|
+
watchIgnore: [
|
|
14
|
+
// ===== MemoryLayer =====
|
|
15
|
+
'**/.memorylayer/**',
|
|
16
|
+
|
|
17
|
+
// ===== Version Control =====
|
|
18
|
+
'**/.git/**',
|
|
19
|
+
'**/.svn/**',
|
|
20
|
+
'**/.hg/**',
|
|
21
|
+
|
|
22
|
+
// ===== Node.js / JavaScript =====
|
|
23
|
+
'**/node_modules/**',
|
|
24
|
+
'**/bower_components/**',
|
|
25
|
+
'**/.npm/**',
|
|
26
|
+
'**/.yarn/**',
|
|
27
|
+
'**/.pnp.*',
|
|
28
|
+
|
|
29
|
+
// ===== Build Outputs =====
|
|
30
|
+
'**/dist/**',
|
|
31
|
+
'**/build/**',
|
|
32
|
+
'**/out/**',
|
|
33
|
+
'**/output/**',
|
|
34
|
+
'**/_build/**',
|
|
35
|
+
'**/public/build/**',
|
|
36
|
+
|
|
37
|
+
// ===== Next.js =====
|
|
38
|
+
'**/.next/**',
|
|
39
|
+
'**/.vercel/**',
|
|
40
|
+
|
|
41
|
+
// ===== Nuxt.js =====
|
|
42
|
+
'**/.nuxt/**',
|
|
43
|
+
'**/.output/**',
|
|
44
|
+
|
|
45
|
+
// ===== Vite / Rollup / Parcel =====
|
|
46
|
+
'**/.vite/**',
|
|
47
|
+
'**/.cache/**',
|
|
48
|
+
'**/.parcel-cache/**',
|
|
49
|
+
'**/.turbo/**',
|
|
50
|
+
'**/.svelte-kit/**',
|
|
51
|
+
|
|
52
|
+
// ===== Python =====
|
|
53
|
+
'**/.venv/**',
|
|
54
|
+
'**/venv/**',
|
|
55
|
+
'**/env/**',
|
|
56
|
+
'**/.env/**',
|
|
57
|
+
'**/virtualenv/**',
|
|
58
|
+
'**/__pycache__/**',
|
|
59
|
+
'**/*.pyc',
|
|
60
|
+
'**/*.pyo',
|
|
61
|
+
'**/*.pyd',
|
|
62
|
+
'**/.Python',
|
|
63
|
+
'**/*.egg-info/**',
|
|
64
|
+
'**/*.egg/**',
|
|
65
|
+
'**/eggs/**',
|
|
66
|
+
'**/.eggs/**',
|
|
67
|
+
'**/pip-wheel-metadata/**',
|
|
68
|
+
'**/.pytest_cache/**',
|
|
69
|
+
'**/.mypy_cache/**',
|
|
70
|
+
'**/.ruff_cache/**',
|
|
71
|
+
'**/.tox/**',
|
|
72
|
+
'**/.nox/**',
|
|
73
|
+
'**/htmlcov/**',
|
|
74
|
+
'**/.coverage',
|
|
75
|
+
'**/.hypothesis/**',
|
|
76
|
+
|
|
77
|
+
// ===== Django =====
|
|
78
|
+
'**/staticfiles/**',
|
|
79
|
+
'**/static_collected/**',
|
|
80
|
+
'**/media/**',
|
|
81
|
+
'**/*.sqlite3',
|
|
82
|
+
'**/db.sqlite3',
|
|
83
|
+
|
|
84
|
+
// ===== FastAPI / Flask =====
|
|
85
|
+
'**/instance/**',
|
|
86
|
+
|
|
87
|
+
// ===== Ruby / Rails =====
|
|
88
|
+
'**/vendor/bundle/**',
|
|
89
|
+
'**/.bundle/**',
|
|
90
|
+
'**/tmp/**',
|
|
91
|
+
'**/log/**',
|
|
92
|
+
|
|
93
|
+
// ===== Go =====
|
|
94
|
+
'**/vendor/**',
|
|
95
|
+
|
|
96
|
+
// ===== Rust =====
|
|
97
|
+
'**/target/**',
|
|
98
|
+
'**/*.rlib',
|
|
99
|
+
|
|
100
|
+
// ===== Java / Kotlin / Gradle / Maven =====
|
|
101
|
+
'**/.gradle/**',
|
|
102
|
+
'**/.mvn/**',
|
|
103
|
+
'**/target/**',
|
|
104
|
+
'**/*.class',
|
|
105
|
+
'**/*.jar',
|
|
106
|
+
'**/*.war',
|
|
107
|
+
|
|
108
|
+
// ===== .NET / C# =====
|
|
109
|
+
'**/bin/**',
|
|
110
|
+
'**/obj/**',
|
|
111
|
+
'**/packages/**',
|
|
112
|
+
'**/*.dll',
|
|
113
|
+
'**/*.exe',
|
|
114
|
+
|
|
115
|
+
// ===== iOS / macOS =====
|
|
116
|
+
'**/Pods/**',
|
|
117
|
+
'**/.build/**',
|
|
118
|
+
'**/DerivedData/**',
|
|
119
|
+
'**/*.xcworkspace/**',
|
|
120
|
+
|
|
121
|
+
// ===== Android =====
|
|
122
|
+
'**/.gradle/**',
|
|
123
|
+
'**/local.properties',
|
|
124
|
+
|
|
125
|
+
// ===== IDE / Editor =====
|
|
126
|
+
'**/.idea/**',
|
|
127
|
+
'**/.vscode/**',
|
|
128
|
+
'**/*.swp',
|
|
129
|
+
'**/*.swo',
|
|
130
|
+
'**/*.sublime-*',
|
|
131
|
+
'**/.project',
|
|
132
|
+
'**/.classpath',
|
|
133
|
+
'**/.settings/**',
|
|
134
|
+
|
|
135
|
+
// ===== Testing / Coverage =====
|
|
136
|
+
'**/coverage/**',
|
|
137
|
+
'**/.nyc_output/**',
|
|
138
|
+
'**/test-results/**',
|
|
139
|
+
'**/jest-cache/**',
|
|
140
|
+
|
|
141
|
+
// ===== Misc Generated / Cache =====
|
|
142
|
+
'**/.DS_Store',
|
|
143
|
+
'**/Thumbs.db',
|
|
144
|
+
'**/*.min.js',
|
|
145
|
+
'**/*.min.css',
|
|
146
|
+
'**/*.map',
|
|
147
|
+
'**/*.chunk.js',
|
|
148
|
+
'**/*.bundle.js',
|
|
149
|
+
|
|
150
|
+
// ===== Lock Files =====
|
|
151
|
+
'**/package-lock.json',
|
|
152
|
+
'**/yarn.lock',
|
|
153
|
+
'**/pnpm-lock.yaml',
|
|
154
|
+
'**/Pipfile.lock',
|
|
155
|
+
'**/poetry.lock',
|
|
156
|
+
'**/Gemfile.lock',
|
|
157
|
+
'**/Cargo.lock',
|
|
158
|
+
'**/composer.lock',
|
|
159
|
+
'**/go.sum',
|
|
160
|
+
|
|
161
|
+
// ===== Environment / Secrets =====
|
|
162
|
+
'**/.env',
|
|
163
|
+
'**/.env.*',
|
|
164
|
+
'**/*.env',
|
|
165
|
+
'**/.envrc',
|
|
166
|
+
'**/secrets/**',
|
|
167
|
+
'**/*.pem',
|
|
168
|
+
'**/*.key',
|
|
169
|
+
|
|
170
|
+
// ===== Logs / Temp =====
|
|
171
|
+
'**/*.log',
|
|
172
|
+
'**/logs/**',
|
|
173
|
+
'**/*.tmp',
|
|
174
|
+
'**/*.temp',
|
|
175
|
+
'**/temp/**'
|
|
176
|
+
]
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function parseArgs(args: string[]): { projectPath: string } {
|
|
181
|
+
let projectPath = process.cwd();
|
|
182
|
+
|
|
183
|
+
for (let i = 0; i < args.length; i++) {
|
|
184
|
+
const arg = args[i];
|
|
185
|
+
const nextArg = args[i + 1];
|
|
186
|
+
if (arg === '--project' && nextArg) {
|
|
187
|
+
projectPath = nextArg;
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return { projectPath: resolve(projectPath) };
|
|
193
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { createHash } from 'crypto';
|
|
2
|
+
import { extname } from 'path';
|
|
3
|
+
|
|
4
|
+
const LANGUAGE_MAP: Record<string, string> = {
|
|
5
|
+
'.ts': 'typescript',
|
|
6
|
+
'.tsx': 'typescript',
|
|
7
|
+
'.js': 'javascript',
|
|
8
|
+
'.jsx': 'javascript',
|
|
9
|
+
'.mjs': 'javascript',
|
|
10
|
+
'.cjs': 'javascript',
|
|
11
|
+
'.py': 'python',
|
|
12
|
+
'.rb': 'ruby',
|
|
13
|
+
'.go': 'go',
|
|
14
|
+
'.rs': 'rust',
|
|
15
|
+
'.java': 'java',
|
|
16
|
+
'.kt': 'kotlin',
|
|
17
|
+
'.scala': 'scala',
|
|
18
|
+
'.cs': 'csharp',
|
|
19
|
+
'.cpp': 'cpp',
|
|
20
|
+
'.cc': 'cpp',
|
|
21
|
+
'.c': 'c',
|
|
22
|
+
'.h': 'c',
|
|
23
|
+
'.hpp': 'cpp',
|
|
24
|
+
'.php': 'php',
|
|
25
|
+
'.swift': 'swift',
|
|
26
|
+
'.m': 'objectivec',
|
|
27
|
+
'.mm': 'objectivec',
|
|
28
|
+
'.vue': 'vue',
|
|
29
|
+
'.svelte': 'svelte',
|
|
30
|
+
'.astro': 'astro',
|
|
31
|
+
'.md': 'markdown',
|
|
32
|
+
'.mdx': 'mdx',
|
|
33
|
+
'.json': 'json',
|
|
34
|
+
'.yaml': 'yaml',
|
|
35
|
+
'.yml': 'yaml',
|
|
36
|
+
'.toml': 'toml',
|
|
37
|
+
'.xml': 'xml',
|
|
38
|
+
'.html': 'html',
|
|
39
|
+
'.htm': 'html',
|
|
40
|
+
'.css': 'css',
|
|
41
|
+
'.scss': 'scss',
|
|
42
|
+
'.sass': 'sass',
|
|
43
|
+
'.less': 'less',
|
|
44
|
+
'.sql': 'sql',
|
|
45
|
+
'.sh': 'bash',
|
|
46
|
+
'.bash': 'bash',
|
|
47
|
+
'.zsh': 'zsh',
|
|
48
|
+
'.fish': 'fish',
|
|
49
|
+
'.ps1': 'powershell',
|
|
50
|
+
'.dockerfile': 'dockerfile',
|
|
51
|
+
'.prisma': 'prisma',
|
|
52
|
+
'.graphql': 'graphql',
|
|
53
|
+
'.gql': 'graphql',
|
|
54
|
+
'.proto': 'protobuf',
|
|
55
|
+
'.tf': 'terraform',
|
|
56
|
+
'.lua': 'lua',
|
|
57
|
+
'.r': 'r',
|
|
58
|
+
'.R': 'r',
|
|
59
|
+
'.jl': 'julia',
|
|
60
|
+
'.ex': 'elixir',
|
|
61
|
+
'.exs': 'elixir',
|
|
62
|
+
'.erl': 'erlang',
|
|
63
|
+
'.hrl': 'erlang',
|
|
64
|
+
'.clj': 'clojure',
|
|
65
|
+
'.cljs': 'clojure',
|
|
66
|
+
'.cljc': 'clojure',
|
|
67
|
+
'.hs': 'haskell',
|
|
68
|
+
'.ml': 'ocaml',
|
|
69
|
+
'.mli': 'ocaml',
|
|
70
|
+
'.fs': 'fsharp',
|
|
71
|
+
'.fsi': 'fsharp',
|
|
72
|
+
'.fsx': 'fsharp',
|
|
73
|
+
'.dart': 'dart',
|
|
74
|
+
'.nim': 'nim',
|
|
75
|
+
'.zig': 'zig',
|
|
76
|
+
'.v': 'v',
|
|
77
|
+
'.odin': 'odin',
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const CODE_EXTENSIONS = new Set(Object.keys(LANGUAGE_MAP));
|
|
81
|
+
|
|
82
|
+
export function detectLanguage(filePath: string): string {
|
|
83
|
+
const ext = extname(filePath).toLowerCase();
|
|
84
|
+
|
|
85
|
+
// Handle special filenames
|
|
86
|
+
const filename = filePath.split(/[/\\]/).pop()?.toLowerCase() || '';
|
|
87
|
+
if (filename === 'dockerfile') return 'dockerfile';
|
|
88
|
+
if (filename === 'makefile') return 'makefile';
|
|
89
|
+
if (filename === 'cmakelists.txt') return 'cmake';
|
|
90
|
+
|
|
91
|
+
return LANGUAGE_MAP[ext] || 'plaintext';
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function isCodeFile(filePath: string): boolean {
|
|
95
|
+
const ext = extname(filePath).toLowerCase();
|
|
96
|
+
const filename = filePath.split(/[/\\]/).pop()?.toLowerCase() || '';
|
|
97
|
+
|
|
98
|
+
return CODE_EXTENSIONS.has(ext) ||
|
|
99
|
+
filename === 'dockerfile' ||
|
|
100
|
+
filename === 'makefile';
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function hashContent(content: string): string {
|
|
104
|
+
return createHash('sha256').update(content).digest('hex');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function getPreview(content: string, maxLength: number = 1000): string {
|
|
108
|
+
if (content.length <= maxLength) {
|
|
109
|
+
return content;
|
|
110
|
+
}
|
|
111
|
+
return content.slice(0, maxLength) + '\n... [truncated]';
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function countLines(content: string): number {
|
|
115
|
+
if (!content) return 0;
|
|
116
|
+
return content.split('\n').length;
|
|
117
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Time utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Format a date as a human-readable "time ago" string
|
|
7
|
+
*/
|
|
8
|
+
export function formatTimeAgo(date: Date): string {
|
|
9
|
+
const now = new Date();
|
|
10
|
+
const diffMs = now.getTime() - date.getTime();
|
|
11
|
+
const diffMins = Math.floor(diffMs / (1000 * 60));
|
|
12
|
+
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
|
|
13
|
+
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
|
|
14
|
+
const diffWeeks = Math.floor(diffDays / 7);
|
|
15
|
+
const diffMonths = Math.floor(diffDays / 30);
|
|
16
|
+
|
|
17
|
+
if (diffMins < 1) return 'just now';
|
|
18
|
+
if (diffMins < 60) return `${diffMins} minute${diffMins > 1 ? 's' : ''} ago`;
|
|
19
|
+
if (diffHours < 24) return `${diffHours} hour${diffHours > 1 ? 's' : ''} ago`;
|
|
20
|
+
if (diffDays === 1) return 'yesterday';
|
|
21
|
+
if (diffDays < 7) return `${diffDays} days ago`;
|
|
22
|
+
if (diffWeeks === 1) return '1 week ago';
|
|
23
|
+
if (diffWeeks < 4) return `${diffWeeks} weeks ago`;
|
|
24
|
+
if (diffMonths === 1) return '1 month ago';
|
|
25
|
+
if (diffMonths < 12) return `${diffMonths} months ago`;
|
|
26
|
+
|
|
27
|
+
return date.toLocaleDateString();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Format a date as "time ago" with an action and file context
|
|
32
|
+
*/
|
|
33
|
+
export function formatTimeAgoWithContext(date: Date, action: string, file: string): string {
|
|
34
|
+
const timeAgo = formatTimeAgo(date);
|
|
35
|
+
const fileName = file.split(/[/\\]/).pop() || file;
|
|
36
|
+
return `You ${action} in ${fileName} ${timeAgo === 'just now' ? 'just now' : timeAgo}`;
|
|
37
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Simple token estimation
|
|
2
|
+
// For more accurate counting, consider using tiktoken or similar
|
|
3
|
+
// This approximation is sufficient for budget management
|
|
4
|
+
|
|
5
|
+
const AVG_CHARS_PER_TOKEN = 4;
|
|
6
|
+
|
|
7
|
+
export function estimateTokens(text: string): number {
|
|
8
|
+
if (!text) return 0;
|
|
9
|
+
// Rough approximation: 1 token ≈ 4 characters for English/code
|
|
10
|
+
// This is a simplification but works well enough for budgeting
|
|
11
|
+
return Math.ceil(text.length / AVG_CHARS_PER_TOKEN);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class TokenBudget {
|
|
15
|
+
private total: number;
|
|
16
|
+
private allocations: Map<string, number> = new Map();
|
|
17
|
+
|
|
18
|
+
constructor(total: number) {
|
|
19
|
+
this.total = total;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
used(): number {
|
|
23
|
+
let sum = 0;
|
|
24
|
+
for (const tokens of this.allocations.values()) {
|
|
25
|
+
sum += tokens;
|
|
26
|
+
}
|
|
27
|
+
return sum;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
remaining(): number {
|
|
31
|
+
return this.total - this.used();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
canFit(text: string): boolean {
|
|
35
|
+
return estimateTokens(text) <= this.remaining();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
allocate(text: string, category: string): boolean {
|
|
39
|
+
const tokens = estimateTokens(text);
|
|
40
|
+
if (tokens > this.remaining()) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const current = this.allocations.get(category) || 0;
|
|
45
|
+
this.allocations.set(category, current + tokens);
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
getAllocations(): Record<string, number> {
|
|
50
|
+
return Object.fromEntries(this.allocations);
|
|
51
|
+
}
|
|
52
|
+
}
|