@comfanion/workflow 4.7.0 → 4.8.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/package.json
CHANGED
package/src/build-info.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Code Index Status & Management Tool
|
|
3
3
|
*
|
|
4
4
|
* Check indexing status and trigger re-indexing.
|
|
5
|
-
*
|
|
5
|
+
* Uses LOCAL vectorizer - no npm calls needed.
|
|
6
6
|
*
|
|
7
7
|
* Usage by model:
|
|
8
8
|
* codeindex({ action: "status" })
|
|
@@ -14,38 +14,78 @@
|
|
|
14
14
|
import { tool } from "@opencode-ai/plugin"
|
|
15
15
|
import path from "path"
|
|
16
16
|
import fs from "fs/promises"
|
|
17
|
-
import { execSync } from "child_process"
|
|
18
17
|
|
|
19
|
-
//
|
|
20
|
-
const
|
|
21
|
-
code:
|
|
22
|
-
docs:
|
|
23
|
-
config:
|
|
18
|
+
// File extensions for each index type
|
|
19
|
+
const INDEX_EXTENSIONS: Record<string, string[]> = {
|
|
20
|
+
code: ['.js', '.ts', '.jsx', '.tsx', '.go', '.py', '.rs', '.java', '.kt', '.swift', '.c', '.cpp', '.h', '.cs', '.rb', '.php'],
|
|
21
|
+
docs: ['.md', '.mdx', '.txt', '.rst', '.adoc'],
|
|
22
|
+
config: ['.yaml', '.yml', '.json', '.toml', '.ini', '.xml'],
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const INDEX_DESCRIPTIONS: Record<string, string> = {
|
|
26
|
+
code: 'Source code files',
|
|
27
|
+
docs: 'Documentation files',
|
|
28
|
+
config: 'Configuration files',
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Simple recursive file walker (no external deps)
|
|
32
|
+
async function walkDir(dir: string, extensions: string[], ignore: string[] = []): Promise<string[]> {
|
|
33
|
+
const files: string[] = []
|
|
34
|
+
|
|
35
|
+
async function walk(currentDir: string) {
|
|
36
|
+
try {
|
|
37
|
+
const entries = await fs.readdir(currentDir, { withFileTypes: true })
|
|
38
|
+
|
|
39
|
+
for (const entry of entries) {
|
|
40
|
+
const fullPath = path.join(currentDir, entry.name)
|
|
41
|
+
const relativePath = path.relative(dir, fullPath)
|
|
42
|
+
|
|
43
|
+
// Skip ignored directories
|
|
44
|
+
if (ignore.some(ig => relativePath.startsWith(ig) || entry.name === ig)) {
|
|
45
|
+
continue
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (entry.isDirectory()) {
|
|
49
|
+
await walk(fullPath)
|
|
50
|
+
} else if (entry.isFile()) {
|
|
51
|
+
const ext = path.extname(entry.name).toLowerCase()
|
|
52
|
+
if (extensions.includes(ext)) {
|
|
53
|
+
files.push(fullPath)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
} catch {}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
await walk(dir)
|
|
61
|
+
return files
|
|
24
62
|
}
|
|
25
63
|
|
|
26
64
|
export default tool({
|
|
27
65
|
description: `Check codebase index status or trigger re-indexing for semantic search.
|
|
28
66
|
|
|
29
67
|
Actions:
|
|
30
|
-
- "status" → Show index statistics
|
|
68
|
+
- "status" → Show index statistics
|
|
31
69
|
- "list" → List all available indexes with stats
|
|
32
|
-
- "reindex" → Re-index files (
|
|
70
|
+
- "reindex" → Re-index files using LOCAL vectorizer (no npm needed)
|
|
33
71
|
|
|
34
72
|
Available indexes:
|
|
35
|
-
- "code" - Source code files
|
|
36
|
-
- "docs" - Documentation files
|
|
37
|
-
- "config" - Configuration files
|
|
73
|
+
- "code" - Source code files (.js, .ts, .go, .py, etc.)
|
|
74
|
+
- "docs" - Documentation files (.md, .txt, etc.)
|
|
75
|
+
- "config" - Configuration files (.yaml, .json, etc.)
|
|
38
76
|
|
|
39
|
-
Note:
|
|
77
|
+
Note: First indexing takes ~30s to load embedding model.`,
|
|
40
78
|
|
|
41
79
|
args: {
|
|
42
80
|
action: tool.schema.enum(["status", "list", "reindex"]).describe("Action to perform"),
|
|
43
81
|
index: tool.schema.string().optional().default("code").describe("Index name: code, docs, config"),
|
|
82
|
+
dir: tool.schema.string().optional().describe("Directory to index (default: project root)"),
|
|
44
83
|
},
|
|
45
84
|
|
|
46
85
|
async execute(args, context) {
|
|
47
86
|
const projectRoot = process.cwd()
|
|
48
87
|
const vectorizerDir = path.join(projectRoot, ".opencode", "vectorizer")
|
|
88
|
+
const vectorizerModule = path.join(vectorizerDir, "index.js")
|
|
49
89
|
const vectorsDir = path.join(projectRoot, ".opencode", "vectors")
|
|
50
90
|
|
|
51
91
|
// Check if vectorizer is installed
|
|
@@ -56,71 +96,59 @@ Note: Initial indexing takes ~30s to load the embedding model.`,
|
|
|
56
96
|
if (!isInstalled) {
|
|
57
97
|
return `❌ Vectorizer not installed.
|
|
58
98
|
|
|
59
|
-
To install:
|
|
99
|
+
To install, run in terminal:
|
|
60
100
|
\`\`\`bash
|
|
61
101
|
npx @comfanion/workflow vectorizer install
|
|
62
102
|
\`\`\`
|
|
63
103
|
|
|
64
|
-
This
|
|
104
|
+
This downloads the embedding model (~100MB).`
|
|
65
105
|
}
|
|
66
106
|
|
|
67
107
|
const indexName = args.index || "code"
|
|
68
108
|
|
|
69
109
|
// LIST: Show all indexes
|
|
70
110
|
if (args.action === "list") {
|
|
111
|
+
let output = `## Codebase Index Overview\n\n`
|
|
112
|
+
output += `✅ **Vectorizer installed**\n\n`
|
|
113
|
+
|
|
114
|
+
const indexes: string[] = []
|
|
71
115
|
try {
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
// Fallback: read hashes files directly
|
|
79
|
-
let output = `## Codebase Index Overview\n\n`
|
|
80
|
-
output += `✅ **Vectorizer installed**\n\n`
|
|
81
|
-
|
|
82
|
-
const indexes: string[] = []
|
|
83
|
-
try {
|
|
84
|
-
const entries = await fs.readdir(vectorsDir, { withFileTypes: true })
|
|
85
|
-
for (const entry of entries) {
|
|
86
|
-
if (entry.isDirectory()) {
|
|
87
|
-
indexes.push(entry.name)
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
} catch {}
|
|
91
|
-
|
|
92
|
-
if (indexes.length === 0) {
|
|
93
|
-
output += `⚠️ **No indexes created yet**\n\n`
|
|
94
|
-
output += `Create indexes with:\n`
|
|
95
|
-
output += `\`\`\`bash\n`
|
|
96
|
-
output += `npx opencode-workflow index --index code\n`
|
|
97
|
-
output += `npx opencode-workflow index --index docs --dir docs/\n`
|
|
98
|
-
output += `\`\`\`\n`
|
|
99
|
-
} else {
|
|
100
|
-
output += `### Active Indexes\n\n`
|
|
101
|
-
for (const idx of indexes) {
|
|
102
|
-
try {
|
|
103
|
-
const hashesPath = path.join(vectorsDir, idx, "hashes.json")
|
|
104
|
-
const hashes = JSON.parse(await fs.readFile(hashesPath, "utf8"))
|
|
105
|
-
const fileCount = Object.keys(hashes).length
|
|
106
|
-
const desc = INDEX_PRESETS[idx]?.description || "Custom index"
|
|
107
|
-
output += `**📁 ${idx}** - ${desc}\n`
|
|
108
|
-
output += ` Files: ${fileCount}\n\n`
|
|
109
|
-
} catch {}
|
|
116
|
+
const entries = await fs.readdir(vectorsDir, { withFileTypes: true })
|
|
117
|
+
for (const entry of entries) {
|
|
118
|
+
if (entry.isDirectory()) {
|
|
119
|
+
indexes.push(entry.name)
|
|
110
120
|
}
|
|
111
121
|
}
|
|
112
|
-
|
|
113
|
-
|
|
122
|
+
} catch {}
|
|
123
|
+
|
|
124
|
+
if (indexes.length === 0) {
|
|
125
|
+
output += `⚠️ **No indexes created yet**\n\n`
|
|
126
|
+
output += `Create indexes:\n`
|
|
114
127
|
output += `\`\`\`\n`
|
|
115
|
-
output += `
|
|
116
|
-
output += `
|
|
117
|
-
output +=
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
128
|
+
output += `codeindex({ action: "reindex", index: "code" })\n`
|
|
129
|
+
output += `codeindex({ action: "reindex", index: "docs", dir: "docs/" })\n`
|
|
130
|
+
output += `\`\`\`\n`
|
|
131
|
+
} else {
|
|
132
|
+
output += `### Active Indexes\n\n`
|
|
133
|
+
for (const idx of indexes) {
|
|
134
|
+
try {
|
|
135
|
+
const hashesPath = path.join(vectorsDir, idx, "hashes.json")
|
|
136
|
+
const hashes = JSON.parse(await fs.readFile(hashesPath, "utf8"))
|
|
137
|
+
const fileCount = Object.keys(hashes).length
|
|
138
|
+
const desc = INDEX_DESCRIPTIONS[idx] || "Custom index"
|
|
139
|
+
output += `**📁 ${idx}** - ${desc}\n`
|
|
140
|
+
output += ` Files: ${fileCount}\n\n`
|
|
141
|
+
} catch {}
|
|
142
|
+
}
|
|
123
143
|
}
|
|
144
|
+
|
|
145
|
+
output += `### Usage\n`
|
|
146
|
+
output += `\`\`\`\n`
|
|
147
|
+
output += `codesearch({ query: "your query", index: "code" })\n`
|
|
148
|
+
output += `codesearch({ query: "how to deploy", index: "docs" })\n`
|
|
149
|
+
output += `\`\`\``
|
|
150
|
+
|
|
151
|
+
return output
|
|
124
152
|
}
|
|
125
153
|
|
|
126
154
|
// STATUS: Show specific index status
|
|
@@ -132,7 +160,7 @@ This will download the embedding model (~100MB) and set up the vector database.`
|
|
|
132
160
|
const hashes = JSON.parse(hashesContent)
|
|
133
161
|
const fileCount = Object.keys(hashes).length
|
|
134
162
|
const sampleFiles = Object.keys(hashes).slice(0, 5)
|
|
135
|
-
const desc =
|
|
163
|
+
const desc = INDEX_DESCRIPTIONS[indexName] || "Custom index"
|
|
136
164
|
|
|
137
165
|
return `## Index Status: "${indexName}"
|
|
138
166
|
|
|
@@ -152,8 +180,8 @@ codesearch({ query: "your search query", index: "${indexName}" })
|
|
|
152
180
|
\`\`\`
|
|
153
181
|
|
|
154
182
|
To re-index:
|
|
155
|
-
\`\`\`
|
|
156
|
-
|
|
183
|
+
\`\`\`
|
|
184
|
+
codeindex({ action: "reindex", index: "${indexName}" })
|
|
157
185
|
\`\`\``
|
|
158
186
|
|
|
159
187
|
} catch {
|
|
@@ -163,33 +191,58 @@ npx opencode-workflow index --index ${indexName}
|
|
|
163
191
|
⚠️ **Index "${indexName}" not created yet**
|
|
164
192
|
|
|
165
193
|
To create this index:
|
|
166
|
-
\`\`\`
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
194
|
+
\`\`\`
|
|
195
|
+
codeindex({ action: "reindex", index: "${indexName}" })
|
|
196
|
+
\`\`\`
|
|
197
|
+
|
|
198
|
+
Or with specific directory:
|
|
199
|
+
\`\`\`
|
|
200
|
+
codeindex({ action: "reindex", index: "${indexName}", dir: "src/" })
|
|
170
201
|
\`\`\``
|
|
171
202
|
}
|
|
172
203
|
}
|
|
173
204
|
|
|
174
|
-
// REINDEX: Re-index using
|
|
205
|
+
// REINDEX: Re-index using LOCAL vectorizer (no npm!)
|
|
175
206
|
if (args.action === "reindex") {
|
|
176
207
|
try {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
208
|
+
// Import local vectorizer
|
|
209
|
+
const { CodebaseIndexer } = await import(`file://${vectorizerModule}`)
|
|
210
|
+
const indexer = await new CodebaseIndexer(projectRoot, indexName).init()
|
|
211
|
+
|
|
212
|
+
// Determine directory and extensions
|
|
213
|
+
const baseDir = args.dir
|
|
214
|
+
? path.resolve(projectRoot, args.dir)
|
|
215
|
+
: projectRoot
|
|
216
|
+
const extensions = INDEX_EXTENSIONS[indexName] || INDEX_EXTENSIONS.code
|
|
217
|
+
|
|
218
|
+
// Find files using simple walker
|
|
219
|
+
const ignoreList = ['node_modules', '.git', 'dist', 'build', '.opencode', 'vendor', '__pycache__']
|
|
220
|
+
const files = await walkDir(baseDir, extensions, ignoreList)
|
|
221
|
+
|
|
222
|
+
let indexed = 0
|
|
223
|
+
let skipped = 0
|
|
224
|
+
|
|
225
|
+
for (const filePath of files) {
|
|
226
|
+
try {
|
|
227
|
+
const wasIndexed = await indexer.indexFile(filePath)
|
|
228
|
+
if (wasIndexed) indexed++
|
|
229
|
+
else skipped++
|
|
230
|
+
} catch {}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Unload model to free memory
|
|
234
|
+
await indexer.unloadModel()
|
|
235
|
+
|
|
236
|
+
const stats = await indexer.getStats()
|
|
188
237
|
|
|
189
238
|
return `## Re-indexing Complete ✅
|
|
190
239
|
|
|
191
240
|
**Index:** ${indexName}
|
|
192
|
-
**
|
|
241
|
+
**Directory:** ${args.dir || "(project root)"}
|
|
242
|
+
**Files found:** ${files.length}
|
|
243
|
+
**Files indexed:** ${indexed}
|
|
244
|
+
**Files unchanged:** ${skipped}
|
|
245
|
+
**Total chunks:** ${stats.chunkCount}
|
|
193
246
|
|
|
194
247
|
You can now use semantic search:
|
|
195
248
|
\`\`\`
|
|
@@ -199,9 +252,9 @@ codesearch({ query: "your search query", index: "${indexName}" })
|
|
|
199
252
|
} catch (error: any) {
|
|
200
253
|
return `❌ Re-indexing failed: ${error.message}
|
|
201
254
|
|
|
202
|
-
|
|
255
|
+
Make sure vectorizer is installed:
|
|
203
256
|
\`\`\`bash
|
|
204
|
-
npx
|
|
257
|
+
npx @comfanion/workflow vectorizer install
|
|
205
258
|
\`\`\``
|
|
206
259
|
}
|
|
207
260
|
}
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
* codesearch({ query: "error handling", searchAll: true })
|
|
13
13
|
*
|
|
14
14
|
* Prerequisites:
|
|
15
|
-
* npx
|
|
16
|
-
* npx
|
|
17
|
-
* npx
|
|
15
|
+
* npx @comfanion/workflow vectorizer install
|
|
16
|
+
* npx @comfanion/workflow index --index code
|
|
17
|
+
* npx @comfanion/workflow index --index docs
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import { tool } from "@opencode-ai/plugin"
|
|
@@ -36,7 +36,7 @@ Examples:
|
|
|
36
36
|
- "how to deploy" with index: "docs" → finds deployment docs
|
|
37
37
|
- "API keys" with index: "config" → finds config with API settings
|
|
38
38
|
|
|
39
|
-
Prerequisites: Run 'npx
|
|
39
|
+
Prerequisites: Run 'npx @comfanion/workflow index --index <name>' first.`,
|
|
40
40
|
|
|
41
41
|
args: {
|
|
42
42
|
query: tool.schema.string().describe("Semantic search query describing what you're looking for"),
|
|
@@ -54,7 +54,7 @@ Prerequisites: Run 'npx opencode-workflow index --index <name>' first.`,
|
|
|
54
54
|
try {
|
|
55
55
|
await fs.access(path.join(vectorizerDir, "node_modules"))
|
|
56
56
|
} catch {
|
|
57
|
-
return `❌ Vectorizer not installed. Run: npx
|
|
57
|
+
return `❌ Vectorizer not installed. Run: npx @comfanion/workflow vectorizer install`
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
try {
|
|
@@ -71,7 +71,7 @@ Prerequisites: Run 'npx opencode-workflow index --index <name>' first.`,
|
|
|
71
71
|
const indexes = await tempIndexer.listIndexes()
|
|
72
72
|
|
|
73
73
|
if (indexes.length === 0) {
|
|
74
|
-
return `❌ No indexes found. Run: npx
|
|
74
|
+
return `❌ No indexes found. Run: npx @comfanion/workflow index --index code`
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
for (const idx of indexes) {
|
|
@@ -90,7 +90,7 @@ Prerequisites: Run 'npx opencode-workflow index --index <name>' first.`,
|
|
|
90
90
|
try {
|
|
91
91
|
await fs.access(hashesFile)
|
|
92
92
|
} catch {
|
|
93
|
-
return `❌ Index "${indexName}" not found. Run: npx
|
|
93
|
+
return `❌ Index "${indexName}" not found. Run: npx @comfanion/workflow index --index ${indexName}`
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
const indexer = await new CodebaseIndexer(projectRoot, indexName).init()
|
|
@@ -100,7 +100,7 @@ Prerequisites: Run 'npx opencode-workflow index --index <name>' first.`,
|
|
|
100
100
|
|
|
101
101
|
if (allResults.length === 0) {
|
|
102
102
|
const scope = args.searchAll ? "any index" : `index "${indexName}"`
|
|
103
|
-
return `No results found in ${scope} for: "${args.query}"\n\nTry:\n- Different keywords\n- Re-index with: npx
|
|
103
|
+
return `No results found in ${scope} for: "${args.query}"\n\nTry:\n- Different keywords\n- Re-index with: npx @comfanion/workflow index --index ${indexName} --force`
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
// Format results for the model
|
|
@@ -128,7 +128,7 @@ Prerequisites: Run 'npx opencode-workflow index --index <name>' first.`,
|
|
|
128
128
|
return output
|
|
129
129
|
|
|
130
130
|
} catch (error: any) {
|
|
131
|
-
return `❌ Search failed: ${error.message}\n\nTry re-indexing: npx
|
|
131
|
+
return `❌ Search failed: ${error.message}\n\nTry re-indexing: npx @comfanion/workflow index --index ${args.index || "code"} --force`
|
|
132
132
|
}
|
|
133
133
|
},
|
|
134
134
|
})
|