k0ntext 3.0.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/LICENSE +21 -0
- package/README.md +623 -0
- package/bin/k0ntext.js +12 -0
- package/dist/agents/cleanup-agent.d.ts +39 -0
- package/dist/agents/cleanup-agent.d.ts.map +1 -0
- package/dist/agents/cleanup-agent.js +56 -0
- package/dist/agents/cleanup-agent.js.map +1 -0
- package/dist/agents/performance-agent.d.ts +37 -0
- package/dist/agents/performance-agent.d.ts.map +1 -0
- package/dist/agents/performance-agent.js +91 -0
- package/dist/agents/performance-agent.js.map +1 -0
- package/dist/analyzer/index.d.ts +5 -0
- package/dist/analyzer/index.d.ts.map +1 -0
- package/dist/analyzer/index.js +5 -0
- package/dist/analyzer/index.js.map +1 -0
- package/dist/analyzer/intelligent-analyzer.d.ts +111 -0
- package/dist/analyzer/intelligent-analyzer.d.ts.map +1 -0
- package/dist/analyzer/intelligent-analyzer.js +537 -0
- package/dist/analyzer/intelligent-analyzer.js.map +1 -0
- package/dist/cli/commands/cleanup.d.ts +3 -0
- package/dist/cli/commands/cleanup.d.ts.map +1 -0
- package/dist/cli/commands/cleanup.js +24 -0
- package/dist/cli/commands/cleanup.js.map +1 -0
- package/dist/cli/commands/export.d.ts +9 -0
- package/dist/cli/commands/export.d.ts.map +1 -0
- package/dist/cli/commands/export.js +72 -0
- package/dist/cli/commands/export.js.map +1 -0
- package/dist/cli/commands/import.d.ts +9 -0
- package/dist/cli/commands/import.d.ts.map +1 -0
- package/dist/cli/commands/import.js +62 -0
- package/dist/cli/commands/import.js.map +1 -0
- package/dist/cli/commands/performance.d.ts +9 -0
- package/dist/cli/commands/performance.d.ts.map +1 -0
- package/dist/cli/commands/performance.js +36 -0
- package/dist/cli/commands/performance.js.map +1 -0
- package/dist/cli/commands/validate.d.ts +9 -0
- package/dist/cli/commands/validate.d.ts.map +1 -0
- package/dist/cli/commands/validate.js +82 -0
- package/dist/cli/commands/validate.js.map +1 -0
- package/dist/cli/commands/watch.d.ts +9 -0
- package/dist/cli/commands/watch.d.ts.map +1 -0
- package/dist/cli/commands/watch.js +72 -0
- package/dist/cli/commands/watch.js.map +1 -0
- package/dist/cli/generate.d.ts +3 -0
- package/dist/cli/generate.d.ts.map +1 -0
- package/dist/cli/generate.js +194 -0
- package/dist/cli/generate.js.map +1 -0
- package/dist/cli/index.d.ts +9 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +448 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/sync.d.ts +26 -0
- package/dist/cli/sync.d.ts.map +1 -0
- package/dist/cli/sync.js +163 -0
- package/dist/cli/sync.js.map +1 -0
- package/dist/config/cleanup-config.d.ts +26 -0
- package/dist/config/cleanup-config.d.ts.map +1 -0
- package/dist/config/cleanup-config.js +21 -0
- package/dist/config/cleanup-config.js.map +1 -0
- package/dist/db/client.d.ts +284 -0
- package/dist/db/client.d.ts.map +1 -0
- package/dist/db/client.js +688 -0
- package/dist/db/client.js.map +1 -0
- package/dist/db/index.d.ts +6 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +6 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/schema.d.ts +41 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +226 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/embeddings/index.d.ts +5 -0
- package/dist/embeddings/index.d.ts.map +1 -0
- package/dist/embeddings/index.js +5 -0
- package/dist/embeddings/index.js.map +1 -0
- package/dist/embeddings/openrouter.d.ts +133 -0
- package/dist/embeddings/openrouter.d.ts.map +1 -0
- package/dist/embeddings/openrouter.js +455 -0
- package/dist/embeddings/openrouter.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp.d.ts +29 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +257 -0
- package/dist/mcp.js.map +1 -0
- package/docs/ARCHIVE/MIGRATE_TO_NEW_REPO.md +222 -0
- package/docs/ARCHIVE/MIGRATE_TO_UNIFIED.md +220 -0
- package/docs/CLEANUP.md +76 -0
- package/docs/MCP_QUICKSTART.md +219 -0
- package/docs/QUICKSTART.md +119 -0
- package/docs/TROUBLESHOOTING.md +611 -0
- package/package.json +100 -0
- package/skills/context-optimize/SKILL.md +86 -0
- package/skills/implement/SKILL.md +150 -0
- package/skills/plan/SKILL.md +143 -0
- package/skills/research/SKILL.md +103 -0
- package/skills/validate/SKILL.md +62 -0
- package/skills/verify-docs/SKILL.md +77 -0
- package/src/agents/cleanup-agent.ts +96 -0
- package/src/agents/performance-agent.ts +117 -0
- package/src/analyzer/index.ts +10 -0
- package/src/analyzer/intelligent-analyzer.ts +640 -0
- package/src/cli/commands/cleanup.ts +26 -0
- package/src/cli/commands/export.ts +82 -0
- package/src/cli/commands/import.ts +73 -0
- package/src/cli/commands/performance.ts +40 -0
- package/src/cli/commands/validate.ts +98 -0
- package/src/cli/commands/watch.ts +83 -0
- package/src/cli/generate.ts +219 -0
- package/src/cli/index.ts +510 -0
- package/src/cli/sync.ts +194 -0
- package/src/config/cleanup-config.ts +42 -0
- package/src/db/client.ts +949 -0
- package/src/db/index.ts +19 -0
- package/src/db/schema.ts +241 -0
- package/src/embeddings/index.ts +11 -0
- package/src/embeddings/openrouter.ts +592 -0
- package/src/index.ts +57 -0
- package/src/mcp.ts +354 -0
- package/templates/AI_CONTEXT.md.template +245 -0
- package/templates/base/README.md +260 -0
- package/templates/base/RPI_WORKFLOW_PLAN.md +325 -0
- package/templates/base/agents/api-developer.md +76 -0
- package/templates/base/agents/context-engineer.md +525 -0
- package/templates/base/agents/core-architect.md +76 -0
- package/templates/base/agents/database-ops.md +76 -0
- package/templates/base/agents/deployment-ops.md +76 -0
- package/templates/base/agents/integration-hub.md +76 -0
- package/templates/base/analytics/README.md +114 -0
- package/templates/base/automation/config.json +58 -0
- package/templates/base/automation/generators/code-mapper.js +308 -0
- package/templates/base/automation/generators/index-builder.js +321 -0
- package/templates/base/automation/hooks/post-commit.sh +83 -0
- package/templates/base/automation/hooks/pre-commit.sh +103 -0
- package/templates/base/ci-templates/README.md +108 -0
- package/templates/base/ci-templates/github-actions/context-check.yml +144 -0
- package/templates/base/ci-templates/github-actions/validate-docs.yml +105 -0
- package/templates/base/commands/analytics.md +238 -0
- package/templates/base/commands/auto-sync.md +172 -0
- package/templates/base/commands/collab.md +194 -0
- package/templates/base/commands/context-optimize.md +226 -0
- package/templates/base/commands/help.md +485 -0
- package/templates/base/commands/rpi-implement.md +164 -0
- package/templates/base/commands/rpi-plan.md +147 -0
- package/templates/base/commands/rpi-research.md +145 -0
- package/templates/base/commands/session-resume.md +144 -0
- package/templates/base/commands/session-save.md +112 -0
- package/templates/base/commands/validate-all.md +77 -0
- package/templates/base/commands/verify-docs-current.md +86 -0
- package/templates/base/config/base.json +57 -0
- package/templates/base/config/environments/development.json +13 -0
- package/templates/base/config/environments/production.json +17 -0
- package/templates/base/config/environments/staging.json +13 -0
- package/templates/base/config/local.json.example +21 -0
- package/templates/base/context/.meta/generated-at.json +18 -0
- package/templates/base/context/ARCHITECTURE_SNAPSHOT.md +156 -0
- package/templates/base/context/CODE_TO_WORKFLOW_MAP.md +94 -0
- package/templates/base/context/FILE_OWNERSHIP.md +57 -0
- package/templates/base/context/INTEGRATION_POINTS.md +92 -0
- package/templates/base/context/KNOWN_GOTCHAS.md +195 -0
- package/templates/base/context/TESTING_MAP.md +95 -0
- package/templates/base/context/WORKFLOW_INDEX.md +129 -0
- package/templates/base/context/workflows/WORKFLOW_TEMPLATE.md +294 -0
- package/templates/base/indexes/agents/CAPABILITY_MATRIX.md +255 -0
- package/templates/base/indexes/agents/CATEGORY_INDEX.md +44 -0
- package/templates/base/indexes/code/CATEGORY_INDEX.md +38 -0
- package/templates/base/indexes/routing/CATEGORY_INDEX.md +39 -0
- package/templates/base/indexes/search/CATEGORY_INDEX.md +39 -0
- package/templates/base/indexes/workflows/CATEGORY_INDEX.md +38 -0
- package/templates/base/knowledge/README.md +98 -0
- package/templates/base/knowledge/sessions/README.md +88 -0
- package/templates/base/knowledge/sessions/TEMPLATE.md +150 -0
- package/templates/base/knowledge/shared/decisions/0001-adopt-context-engineering.md +144 -0
- package/templates/base/knowledge/shared/decisions/README.md +49 -0
- package/templates/base/knowledge/shared/decisions/TEMPLATE.md +123 -0
- package/templates/base/knowledge/shared/patterns/README.md +62 -0
- package/templates/base/knowledge/shared/patterns/TEMPLATE.md +120 -0
- package/templates/base/plans/PLAN_TEMPLATE.md +316 -0
- package/templates/base/plans/active/.gitkeep +0 -0
- package/templates/base/plans/completed/.gitkeep +0 -0
- package/templates/base/research/RESEARCH_TEMPLATE.md +245 -0
- package/templates/base/research/active/.gitkeep +0 -0
- package/templates/base/research/completed/.gitkeep +0 -0
- package/templates/base/schemas/agent.schema.json +141 -0
- package/templates/base/schemas/anchors.schema.json +54 -0
- package/templates/base/schemas/automation.schema.json +93 -0
- package/templates/base/schemas/command.schema.json +134 -0
- package/templates/base/schemas/hashes.schema.json +40 -0
- package/templates/base/schemas/manifest.schema.json +117 -0
- package/templates/base/schemas/plan.schema.json +136 -0
- package/templates/base/schemas/research.schema.json +115 -0
- package/templates/base/schemas/roles.schema.json +34 -0
- package/templates/base/schemas/session.schema.json +77 -0
- package/templates/base/schemas/settings.schema.json +244 -0
- package/templates/base/schemas/staleness.schema.json +53 -0
- package/templates/base/schemas/team-config.schema.json +42 -0
- package/templates/base/schemas/workflow.schema.json +126 -0
- package/templates/base/session/checkpoints/.gitkeep +2 -0
- package/templates/base/session/current/state.json +20 -0
- package/templates/base/session/history/.gitkeep +2 -0
- package/templates/base/settings.json +3 -0
- package/templates/base/standards/COMPATIBILITY.md +219 -0
- package/templates/base/standards/EXTENSION_GUIDELINES.md +280 -0
- package/templates/base/standards/QUALITY_CHECKLIST.md +211 -0
- package/templates/base/standards/README.md +66 -0
- package/templates/base/sync/anchors.json +6 -0
- package/templates/base/sync/hashes.json +6 -0
- package/templates/base/sync/staleness.json +10 -0
- package/templates/base/team/README.md +168 -0
- package/templates/base/team/config.json +79 -0
- package/templates/base/team/roles.json +145 -0
- package/templates/base/tools/bin/claude-context.js +151 -0
- package/templates/base/tools/lib/anchor-resolver.js +276 -0
- package/templates/base/tools/lib/config-loader.js +363 -0
- package/templates/base/tools/lib/detector.js +350 -0
- package/templates/base/tools/lib/diagnose.js +206 -0
- package/templates/base/tools/lib/drift-detector.js +373 -0
- package/templates/base/tools/lib/errors.js +199 -0
- package/templates/base/tools/lib/index.js +36 -0
- package/templates/base/tools/lib/init.js +192 -0
- package/templates/base/tools/lib/logger.js +230 -0
- package/templates/base/tools/lib/placeholder.js +201 -0
- package/templates/base/tools/lib/session-manager.js +354 -0
- package/templates/base/tools/lib/validate.js +521 -0
- package/templates/base/tools/package.json +49 -0
- package/templates/handlebars/aider-config.hbs +146 -0
- package/templates/handlebars/antigravity.hbs +377 -0
- package/templates/handlebars/claude.hbs +183 -0
- package/templates/handlebars/cline.hbs +62 -0
- package/templates/handlebars/continue-config.hbs +116 -0
- package/templates/handlebars/copilot.hbs +130 -0
- package/templates/handlebars/partials/gotcha-list.hbs +11 -0
- package/templates/handlebars/partials/header.hbs +3 -0
- package/templates/handlebars/partials/workflow-summary.hbs +16 -0
- package/templates/handlebars/windsurf-rules.hbs +69 -0
- package/templates/hooks/post-commit.hbs +28 -0
- package/templates/hooks/pre-commit.hbs +46 -0
|
@@ -0,0 +1,640 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intelligent Analyzer
|
|
3
|
+
*
|
|
4
|
+
* Uses OpenRouter API to intelligently analyze codebases, docs, and tool configurations.
|
|
5
|
+
* This is the core of the "forcefully intelligent" initialization.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { glob } from 'glob';
|
|
11
|
+
import { OpenRouterClient, createOpenRouterClient, hasOpenRouterKey } from '../embeddings/openrouter.js';
|
|
12
|
+
import { AI_TOOLS, AI_TOOL_FOLDERS, type AITool } from '../db/schema.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Discovery result for a file
|
|
16
|
+
*/
|
|
17
|
+
export interface DiscoveredFile {
|
|
18
|
+
path: string;
|
|
19
|
+
relativePath: string;
|
|
20
|
+
type: 'doc' | 'code' | 'config' | 'tool_config';
|
|
21
|
+
tool?: AITool;
|
|
22
|
+
size: number;
|
|
23
|
+
content?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Analysis result
|
|
28
|
+
*/
|
|
29
|
+
export interface AnalysisResult {
|
|
30
|
+
summary: string;
|
|
31
|
+
techStack: {
|
|
32
|
+
languages: string[];
|
|
33
|
+
frameworks: string[];
|
|
34
|
+
tools: string[];
|
|
35
|
+
};
|
|
36
|
+
workflows: Array<{
|
|
37
|
+
name: string;
|
|
38
|
+
description: string;
|
|
39
|
+
entryPoint: string;
|
|
40
|
+
steps: string[];
|
|
41
|
+
}>;
|
|
42
|
+
architecture: {
|
|
43
|
+
pattern: string;
|
|
44
|
+
components: string[];
|
|
45
|
+
integrations: string[];
|
|
46
|
+
};
|
|
47
|
+
existingContext: {
|
|
48
|
+
tools: AITool[];
|
|
49
|
+
files: DiscoveredFile[];
|
|
50
|
+
};
|
|
51
|
+
suggestions: {
|
|
52
|
+
contextFiles: string[];
|
|
53
|
+
workflows: string[];
|
|
54
|
+
agents: string[];
|
|
55
|
+
commands: string[];
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Default ignore patterns
|
|
61
|
+
*/
|
|
62
|
+
const DEFAULT_IGNORE = [
|
|
63
|
+
'**/node_modules/**',
|
|
64
|
+
'**/dist/**',
|
|
65
|
+
'**/build/**',
|
|
66
|
+
'**/.git/**',
|
|
67
|
+
'**/vendor/**',
|
|
68
|
+
'**/__pycache__/**',
|
|
69
|
+
'**/target/**',
|
|
70
|
+
'**/bin/**',
|
|
71
|
+
'**/obj/**',
|
|
72
|
+
'**/.next/**',
|
|
73
|
+
'**/.nuxt/**',
|
|
74
|
+
'**/*.min.js',
|
|
75
|
+
'**/*.map',
|
|
76
|
+
'**/package-lock.json',
|
|
77
|
+
'**/yarn.lock',
|
|
78
|
+
'**/pnpm-lock.yaml'
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Intelligent analyzer using OpenRouter
|
|
83
|
+
*/
|
|
84
|
+
export class IntelligentAnalyzer {
|
|
85
|
+
private client: OpenRouterClient | null = null;
|
|
86
|
+
private projectRoot: string;
|
|
87
|
+
|
|
88
|
+
constructor(projectRoot: string) {
|
|
89
|
+
this.projectRoot = projectRoot;
|
|
90
|
+
|
|
91
|
+
// Try to create client if API key available
|
|
92
|
+
if (hasOpenRouterKey()) {
|
|
93
|
+
try {
|
|
94
|
+
this.client = createOpenRouterClient();
|
|
95
|
+
} catch (error) {
|
|
96
|
+
console.warn('OpenRouter client initialization failed:', error);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Check if intelligent analysis is available
|
|
103
|
+
*/
|
|
104
|
+
isIntelligentModeAvailable(): boolean {
|
|
105
|
+
return this.client !== null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Discover all documentation files (.md files)
|
|
110
|
+
*/
|
|
111
|
+
async discoverDocs(): Promise<DiscoveredFile[]> {
|
|
112
|
+
const files: DiscoveredFile[] = [];
|
|
113
|
+
|
|
114
|
+
const mdFiles = await glob('**/*.md', {
|
|
115
|
+
cwd: this.projectRoot,
|
|
116
|
+
ignore: DEFAULT_IGNORE,
|
|
117
|
+
absolute: true
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
for (const filePath of mdFiles) {
|
|
121
|
+
const stats = fs.statSync(filePath);
|
|
122
|
+
files.push({
|
|
123
|
+
path: filePath,
|
|
124
|
+
relativePath: path.relative(this.projectRoot, filePath),
|
|
125
|
+
type: 'doc',
|
|
126
|
+
size: stats.size
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return files;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Discover AI tool configurations
|
|
135
|
+
*/
|
|
136
|
+
async discoverToolConfigs(): Promise<DiscoveredFile[]> {
|
|
137
|
+
const files: DiscoveredFile[] = [];
|
|
138
|
+
|
|
139
|
+
for (const tool of AI_TOOLS) {
|
|
140
|
+
const patterns = AI_TOOL_FOLDERS[tool];
|
|
141
|
+
|
|
142
|
+
for (const pattern of patterns) {
|
|
143
|
+
const fullPath = path.join(this.projectRoot, pattern);
|
|
144
|
+
|
|
145
|
+
if (fs.existsSync(fullPath)) {
|
|
146
|
+
const stats = fs.statSync(fullPath);
|
|
147
|
+
|
|
148
|
+
if (stats.isDirectory()) {
|
|
149
|
+
// Scan directory contents
|
|
150
|
+
const dirFiles = await glob('**/*', {
|
|
151
|
+
cwd: fullPath,
|
|
152
|
+
nodir: true,
|
|
153
|
+
absolute: true
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
for (const filePath of dirFiles) {
|
|
157
|
+
const fileStats = fs.statSync(filePath);
|
|
158
|
+
files.push({
|
|
159
|
+
path: filePath,
|
|
160
|
+
relativePath: path.relative(this.projectRoot, filePath),
|
|
161
|
+
type: 'tool_config',
|
|
162
|
+
tool,
|
|
163
|
+
size: fileStats.size
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
} else {
|
|
167
|
+
files.push({
|
|
168
|
+
path: fullPath,
|
|
169
|
+
relativePath: pattern,
|
|
170
|
+
type: 'tool_config',
|
|
171
|
+
tool,
|
|
172
|
+
size: stats.size
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return files;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Discover source code files
|
|
184
|
+
*/
|
|
185
|
+
async discoverCode(): Promise<DiscoveredFile[]> {
|
|
186
|
+
const files: DiscoveredFile[] = [];
|
|
187
|
+
|
|
188
|
+
const codePatterns = [
|
|
189
|
+
'**/*.js', '**/*.ts', '**/*.tsx', '**/*.jsx',
|
|
190
|
+
'**/*.py', '**/*.go', '**/*.rs', '**/*.java',
|
|
191
|
+
'**/*.cs', '**/*.rb', '**/*.php'
|
|
192
|
+
];
|
|
193
|
+
|
|
194
|
+
for (const pattern of codePatterns) {
|
|
195
|
+
const codeFiles = await glob(pattern, {
|
|
196
|
+
cwd: this.projectRoot,
|
|
197
|
+
ignore: DEFAULT_IGNORE,
|
|
198
|
+
absolute: true
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
for (const filePath of codeFiles) {
|
|
202
|
+
const stats = fs.statSync(filePath);
|
|
203
|
+
files.push({
|
|
204
|
+
path: filePath,
|
|
205
|
+
relativePath: path.relative(this.projectRoot, filePath),
|
|
206
|
+
type: 'code',
|
|
207
|
+
size: stats.size
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return files;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Read file content safely
|
|
217
|
+
*/
|
|
218
|
+
private readFileContent(filePath: string, maxBytes: number = 50000): string | null {
|
|
219
|
+
let fd: number | null = null;
|
|
220
|
+
try {
|
|
221
|
+
const stats = fs.statSync(filePath);
|
|
222
|
+
if (stats.size > maxBytes) {
|
|
223
|
+
// Read only first part of large files
|
|
224
|
+
fd = fs.openSync(filePath, 'r');
|
|
225
|
+
const buffer = Buffer.alloc(maxBytes);
|
|
226
|
+
fs.readSync(fd, buffer, 0, maxBytes, 0);
|
|
227
|
+
return buffer.toString('utf-8') + '\n\n... [truncated]';
|
|
228
|
+
}
|
|
229
|
+
return fs.readFileSync(filePath, 'utf-8');
|
|
230
|
+
} catch {
|
|
231
|
+
return null;
|
|
232
|
+
} finally {
|
|
233
|
+
if (fd !== null) {
|
|
234
|
+
try {
|
|
235
|
+
fs.closeSync(fd);
|
|
236
|
+
} catch {
|
|
237
|
+
// Ignore errors when closing the file descriptor
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Perform full intelligent analysis
|
|
245
|
+
*/
|
|
246
|
+
async analyze(): Promise<AnalysisResult> {
|
|
247
|
+
// Discover all files
|
|
248
|
+
const [docs, toolConfigs, codeFiles] = await Promise.all([
|
|
249
|
+
this.discoverDocs(),
|
|
250
|
+
this.discoverToolConfigs(),
|
|
251
|
+
this.discoverCode()
|
|
252
|
+
]);
|
|
253
|
+
|
|
254
|
+
// Determine which AI tools are already configured
|
|
255
|
+
const configuredTools = new Set<AITool>();
|
|
256
|
+
for (const config of toolConfigs) {
|
|
257
|
+
if (config.tool) {
|
|
258
|
+
configuredTools.add(config.tool);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Basic analysis (without AI)
|
|
263
|
+
const basicResult: AnalysisResult = {
|
|
264
|
+
summary: '',
|
|
265
|
+
techStack: this.detectTechStackBasic(codeFiles),
|
|
266
|
+
workflows: [],
|
|
267
|
+
architecture: {
|
|
268
|
+
pattern: 'unknown',
|
|
269
|
+
components: [],
|
|
270
|
+
integrations: []
|
|
271
|
+
},
|
|
272
|
+
existingContext: {
|
|
273
|
+
tools: Array.from(configuredTools),
|
|
274
|
+
files: [...docs, ...toolConfigs]
|
|
275
|
+
},
|
|
276
|
+
suggestions: {
|
|
277
|
+
contextFiles: [],
|
|
278
|
+
workflows: [],
|
|
279
|
+
agents: [],
|
|
280
|
+
commands: []
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
// If OpenRouter is available, perform intelligent analysis
|
|
285
|
+
if (this.client) {
|
|
286
|
+
try {
|
|
287
|
+
const intelligentResult = await this.performIntelligentAnalysis(docs, toolConfigs, codeFiles);
|
|
288
|
+
return {
|
|
289
|
+
...basicResult,
|
|
290
|
+
...intelligentResult,
|
|
291
|
+
existingContext: basicResult.existingContext
|
|
292
|
+
};
|
|
293
|
+
} catch (error) {
|
|
294
|
+
console.warn('Intelligent analysis failed, falling back to basic:', error);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Generate basic summary
|
|
299
|
+
basicResult.summary = this.generateBasicSummary(docs, toolConfigs, codeFiles, basicResult.techStack);
|
|
300
|
+
|
|
301
|
+
return basicResult;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Detect tech stack without AI
|
|
306
|
+
*/
|
|
307
|
+
private detectTechStackBasic(codeFiles: DiscoveredFile[]): AnalysisResult['techStack'] {
|
|
308
|
+
const languages = new Set<string>();
|
|
309
|
+
const frameworks = new Set<string>();
|
|
310
|
+
const tools = new Set<string>();
|
|
311
|
+
|
|
312
|
+
// Detect languages from file extensions
|
|
313
|
+
for (const file of codeFiles) {
|
|
314
|
+
const ext = path.extname(file.relativePath).toLowerCase();
|
|
315
|
+
switch (ext) {
|
|
316
|
+
case '.js':
|
|
317
|
+
case '.mjs':
|
|
318
|
+
case '.cjs':
|
|
319
|
+
languages.add('JavaScript');
|
|
320
|
+
break;
|
|
321
|
+
case '.ts':
|
|
322
|
+
case '.tsx':
|
|
323
|
+
languages.add('TypeScript');
|
|
324
|
+
break;
|
|
325
|
+
case '.py':
|
|
326
|
+
languages.add('Python');
|
|
327
|
+
break;
|
|
328
|
+
case '.go':
|
|
329
|
+
languages.add('Go');
|
|
330
|
+
break;
|
|
331
|
+
case '.rs':
|
|
332
|
+
languages.add('Rust');
|
|
333
|
+
break;
|
|
334
|
+
case '.java':
|
|
335
|
+
languages.add('Java');
|
|
336
|
+
break;
|
|
337
|
+
case '.cs':
|
|
338
|
+
languages.add('C#');
|
|
339
|
+
break;
|
|
340
|
+
case '.rb':
|
|
341
|
+
languages.add('Ruby');
|
|
342
|
+
break;
|
|
343
|
+
case '.php':
|
|
344
|
+
languages.add('PHP');
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Check for common framework indicators
|
|
350
|
+
const packageJsonPath = path.join(this.projectRoot, 'package.json');
|
|
351
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
352
|
+
try {
|
|
353
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
354
|
+
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
355
|
+
|
|
356
|
+
if (deps.react) frameworks.add('React');
|
|
357
|
+
if (deps.vue) frameworks.add('Vue');
|
|
358
|
+
if (deps.angular || deps['@angular/core']) frameworks.add('Angular');
|
|
359
|
+
if (deps.express) frameworks.add('Express');
|
|
360
|
+
if (deps.fastify) frameworks.add('Fastify');
|
|
361
|
+
if (deps.next) frameworks.add('Next.js');
|
|
362
|
+
if (deps.nest || deps['@nestjs/core']) frameworks.add('NestJS');
|
|
363
|
+
|
|
364
|
+
tools.add('npm');
|
|
365
|
+
} catch {
|
|
366
|
+
// Ignore parse errors
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Check for Python indicators
|
|
371
|
+
const requirementsTxtPath = path.join(this.projectRoot, 'requirements.txt');
|
|
372
|
+
const pyprojectPath = path.join(this.projectRoot, 'pyproject.toml');
|
|
373
|
+
if (fs.existsSync(requirementsTxtPath) || fs.existsSync(pyprojectPath)) {
|
|
374
|
+
tools.add('pip');
|
|
375
|
+
if (fs.existsSync(pyprojectPath)) {
|
|
376
|
+
tools.add('poetry');
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return {
|
|
381
|
+
languages: Array.from(languages),
|
|
382
|
+
frameworks: Array.from(frameworks),
|
|
383
|
+
tools: Array.from(tools)
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Perform intelligent analysis using OpenRouter
|
|
389
|
+
*/
|
|
390
|
+
private async performIntelligentAnalysis(
|
|
391
|
+
docs: DiscoveredFile[],
|
|
392
|
+
toolConfigs: DiscoveredFile[],
|
|
393
|
+
codeFiles: DiscoveredFile[]
|
|
394
|
+
): Promise<Partial<AnalysisResult>> {
|
|
395
|
+
if (!this.client) {
|
|
396
|
+
throw new Error('OpenRouter client not available');
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Gather sample content for analysis
|
|
400
|
+
const sampleContent: string[] = [];
|
|
401
|
+
|
|
402
|
+
// Add README and main docs
|
|
403
|
+
const importantDocs = docs.filter(d =>
|
|
404
|
+
d.relativePath.toLowerCase().includes('readme') ||
|
|
405
|
+
d.relativePath.toLowerCase().includes('claude') ||
|
|
406
|
+
d.relativePath.toLowerCase().includes('context')
|
|
407
|
+
).slice(0, 5);
|
|
408
|
+
|
|
409
|
+
for (const doc of importantDocs) {
|
|
410
|
+
const content = this.readFileContent(doc.path, 10000);
|
|
411
|
+
if (content) {
|
|
412
|
+
sampleContent.push(`## File: ${doc.relativePath}\n\n${content}`);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Add tool configs
|
|
417
|
+
for (const config of toolConfigs.slice(0, 5)) {
|
|
418
|
+
const content = this.readFileContent(config.path, 5000);
|
|
419
|
+
if (content) {
|
|
420
|
+
sampleContent.push(`## Tool Config (${config.tool}): ${config.relativePath}\n\n${content}`);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Add sample code files (entry points)
|
|
425
|
+
const entryPoints = codeFiles.filter(f =>
|
|
426
|
+
f.relativePath.includes('index.') ||
|
|
427
|
+
f.relativePath.includes('main.') ||
|
|
428
|
+
f.relativePath.includes('app.') ||
|
|
429
|
+
f.relativePath.includes('server.')
|
|
430
|
+
).slice(0, 5);
|
|
431
|
+
|
|
432
|
+
for (const code of entryPoints) {
|
|
433
|
+
const content = this.readFileContent(code.path, 8000);
|
|
434
|
+
if (content) {
|
|
435
|
+
sampleContent.push(`## Code: ${code.relativePath}\n\n${content}`);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Create analysis prompt
|
|
440
|
+
const analysisPrompt = `
|
|
441
|
+
Analyze this codebase content and provide a structured analysis:
|
|
442
|
+
|
|
443
|
+
${sampleContent.join('\n\n---\n\n')}
|
|
444
|
+
|
|
445
|
+
Provide your analysis in the following JSON format:
|
|
446
|
+
{
|
|
447
|
+
"summary": "Brief description of what this project does",
|
|
448
|
+
"techStack": {
|
|
449
|
+
"languages": ["list of programming languages"],
|
|
450
|
+
"frameworks": ["list of frameworks"],
|
|
451
|
+
"tools": ["list of tools and libraries"]
|
|
452
|
+
},
|
|
453
|
+
"workflows": [
|
|
454
|
+
{
|
|
455
|
+
"name": "workflow name",
|
|
456
|
+
"description": "what it does",
|
|
457
|
+
"entryPoint": "file:line",
|
|
458
|
+
"steps": ["step 1", "step 2"]
|
|
459
|
+
}
|
|
460
|
+
],
|
|
461
|
+
"architecture": {
|
|
462
|
+
"pattern": "architectural pattern (e.g., MVC, microservices)",
|
|
463
|
+
"components": ["main components"],
|
|
464
|
+
"integrations": ["external integrations"]
|
|
465
|
+
},
|
|
466
|
+
"suggestions": {
|
|
467
|
+
"contextFiles": ["suggested context files to create"],
|
|
468
|
+
"workflows": ["workflows to document"],
|
|
469
|
+
"agents": ["agents that would be useful"],
|
|
470
|
+
"commands": ["commands to implement"]
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
Return ONLY valid JSON, no markdown formatting.
|
|
475
|
+
`;
|
|
476
|
+
|
|
477
|
+
let response: string = '';
|
|
478
|
+
try {
|
|
479
|
+
response = await this.client.chat([
|
|
480
|
+
{
|
|
481
|
+
role: 'system',
|
|
482
|
+
content: 'You are an expert code analyzer and AI context engineer. Analyze codebases to understand their structure, workflows, and suggest optimal AI context configurations. Always return valid JSON.'
|
|
483
|
+
},
|
|
484
|
+
{ role: 'user', content: analysisPrompt }
|
|
485
|
+
], { temperature: 0.2, maxTokens: 4096 });
|
|
486
|
+
|
|
487
|
+
// First, try to parse the whole response as JSON.
|
|
488
|
+
try {
|
|
489
|
+
return JSON.parse(response);
|
|
490
|
+
} catch {
|
|
491
|
+
// If that fails, attempt to extract the JSON substring between the
|
|
492
|
+
// first '{' and the last '}' and parse that.
|
|
493
|
+
if (typeof response === 'string') {
|
|
494
|
+
const start = response.indexOf('{');
|
|
495
|
+
const end = response.lastIndexOf('}');
|
|
496
|
+
|
|
497
|
+
if (start !== -1 && end !== -1 && end > start) {
|
|
498
|
+
const jsonSubstring = response.slice(start, end + 1);
|
|
499
|
+
return JSON.parse(jsonSubstring);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
} catch (error) {
|
|
504
|
+
console.warn('Failed to parse intelligent analysis response.');
|
|
505
|
+
// Log a preview of the raw response to aid debugging of malformed JSON.
|
|
506
|
+
try {
|
|
507
|
+
const preview = typeof response === 'string'
|
|
508
|
+
? response.slice(0, 1000)
|
|
509
|
+
: JSON.stringify(response).slice(0, 1000);
|
|
510
|
+
console.warn('Raw response preview (truncated to 1000 chars):', preview);
|
|
511
|
+
} catch {
|
|
512
|
+
// If preview logging fails for any reason, ignore and just log the error.
|
|
513
|
+
}
|
|
514
|
+
console.warn('Error details:', error);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
return {};
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Generate basic summary without AI
|
|
522
|
+
*/
|
|
523
|
+
private generateBasicSummary(
|
|
524
|
+
docs: DiscoveredFile[],
|
|
525
|
+
toolConfigs: DiscoveredFile[],
|
|
526
|
+
codeFiles: DiscoveredFile[],
|
|
527
|
+
techStack: AnalysisResult['techStack']
|
|
528
|
+
): string {
|
|
529
|
+
const parts: string[] = [];
|
|
530
|
+
|
|
531
|
+
parts.push(`Project Analysis Summary`);
|
|
532
|
+
parts.push(`========================`);
|
|
533
|
+
parts.push(``);
|
|
534
|
+
parts.push(`**Files Discovered:**`);
|
|
535
|
+
parts.push(`- Documentation: ${docs.length} files`);
|
|
536
|
+
parts.push(`- Source Code: ${codeFiles.length} files`);
|
|
537
|
+
parts.push(`- AI Tool Configs: ${toolConfigs.length} files`);
|
|
538
|
+
parts.push(``);
|
|
539
|
+
parts.push(`**Tech Stack:**`);
|
|
540
|
+
if (techStack.languages.length > 0) {
|
|
541
|
+
parts.push(`- Languages: ${techStack.languages.join(', ')}`);
|
|
542
|
+
}
|
|
543
|
+
if (techStack.frameworks.length > 0) {
|
|
544
|
+
parts.push(`- Frameworks: ${techStack.frameworks.join(', ')}`);
|
|
545
|
+
}
|
|
546
|
+
if (techStack.tools.length > 0) {
|
|
547
|
+
parts.push(`- Tools: ${techStack.tools.join(', ')}`);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
return parts.join('\n');
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Generate embeddings for discovered content
|
|
555
|
+
*/
|
|
556
|
+
async generateEmbeddings(files: DiscoveredFile[]): Promise<Map<string, number[]>> {
|
|
557
|
+
if (!this.client) {
|
|
558
|
+
throw new Error('OpenRouter client not available for embeddings');
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
const embeddings = new Map<string, number[]>();
|
|
562
|
+
|
|
563
|
+
// Process in batches
|
|
564
|
+
const batchSize = 10;
|
|
565
|
+
for (let i = 0; i < files.length; i += batchSize) {
|
|
566
|
+
const batch = files.slice(i, i + batchSize);
|
|
567
|
+
const texts: string[] = [];
|
|
568
|
+
const paths: string[] = [];
|
|
569
|
+
|
|
570
|
+
for (const file of batch) {
|
|
571
|
+
const content = this.readFileContent(file.path, 8000);
|
|
572
|
+
if (content) {
|
|
573
|
+
texts.push(`File: ${file.relativePath}\n\n${content}`);
|
|
574
|
+
paths.push(file.relativePath);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
if (texts.length > 0) {
|
|
579
|
+
const batchEmbeddings = await this.client.embedBatch(texts);
|
|
580
|
+
for (let j = 0; j < paths.length; j++) {
|
|
581
|
+
embeddings.set(paths[j], batchEmbeddings[j]);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
return embeddings;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* Generate embedding for a single text string (e.g., search query)
|
|
591
|
+
*/
|
|
592
|
+
async embedText(text: string): Promise<number[]> {
|
|
593
|
+
if (!this.client) {
|
|
594
|
+
throw new Error('OpenRouter client not available for embeddings');
|
|
595
|
+
}
|
|
596
|
+
return this.client.embed(text);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* Set up file watcher for automatic re-indexing
|
|
601
|
+
*/
|
|
602
|
+
setupFileWatcher(onChange: (filePath: string) => Promise<void>): () => void {
|
|
603
|
+
// Check if chokidar is available
|
|
604
|
+
let chokidar: unknown;
|
|
605
|
+
try {
|
|
606
|
+
chokidar = require('chokidar');
|
|
607
|
+
} catch {
|
|
608
|
+
console.warn('chokidar not available. File watching disabled. Install with: npm install chokidar');
|
|
609
|
+
return () => {};
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
const watchPaths = [
|
|
613
|
+
path.join(this.projectRoot, 'src'),
|
|
614
|
+
path.join(this.projectRoot, 'lib'),
|
|
615
|
+
path.join(this.projectRoot, '.claude'),
|
|
616
|
+
path.join(this.projectRoot, 'CLAUDE.md'),
|
|
617
|
+
path.join(this.projectRoot, 'README.md'),
|
|
618
|
+
path.join(this.projectRoot, 'docs')
|
|
619
|
+
];
|
|
620
|
+
|
|
621
|
+
const watcher = (chokidar as any).watch(watchPaths, {
|
|
622
|
+
ignored: /node_modules|\.git|dist/,
|
|
623
|
+
persistent: true,
|
|
624
|
+
ignoreInitial: true
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
watcher.on('change', async (filePath: string) => {
|
|
628
|
+
await onChange(filePath);
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
return () => watcher.close();
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* Create an intelligent analyzer
|
|
637
|
+
*/
|
|
638
|
+
export function createIntelligentAnalyzer(projectRoot: string): IntelligentAnalyzer {
|
|
639
|
+
return new IntelligentAnalyzer(projectRoot);
|
|
640
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { CleanupAgent } from '../../agents/cleanup-agent.js';
|
|
3
|
+
|
|
4
|
+
export const cleanupCommand = new Command('cleanup')
|
|
5
|
+
.description('Clean up context folders from other AI tools')
|
|
6
|
+
.option('--dry-run', 'Show what would be removed')
|
|
7
|
+
.option('--keep <folders>', 'Folders to keep (comma-separated)')
|
|
8
|
+
.option('-v, --verbose', 'Show detailed output')
|
|
9
|
+
.action(async (options) => {
|
|
10
|
+
const agent = new CleanupAgent();
|
|
11
|
+
// Pass only the options that have values
|
|
12
|
+
const result = await agent.cleanup({
|
|
13
|
+
dryRun: !!options.dryRun,
|
|
14
|
+
keep: options.keep ? options.keep.split(',') : undefined,
|
|
15
|
+
verbose: !!options.verbose,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
console.log(`\nCleanup complete:`);
|
|
19
|
+
console.log(` Scanned: ${result.scanned} tool folders`);
|
|
20
|
+
console.log(` Removed: ${result.removed.length}`);
|
|
21
|
+
console.log(` Kept: ${result.kept.length}`);
|
|
22
|
+
|
|
23
|
+
if (result.errors.length > 0) {
|
|
24
|
+
console.log(` Errors: ${result.errors.length}`);
|
|
25
|
+
}
|
|
26
|
+
});
|