@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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@comfanion/workflow",
3
- "version": "4.7.0",
3
+ "version": "4.8.0",
4
4
  "description": "Initialize OpenCode Workflow system for AI-assisted development with semantic code search",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": "3.0.0",
3
- "buildDate": "2026-01-24T09:24:45.283Z",
3
+ "buildDate": "2026-01-24T09:31:08.971Z",
4
4
  "files": [
5
5
  "config.yaml",
6
6
  "FLOW.yaml",
@@ -2,7 +2,7 @@
2
2
  * Code Index Status & Management Tool
3
3
  *
4
4
  * Check indexing status and trigger re-indexing.
5
- * Supports multiple indexes: code, docs, config.
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
- // Index presets for documentation
20
- const INDEX_PRESETS: Record<string, { pattern: string; description: string }> = {
21
- code: { pattern: '**/*.{js,ts,go,py,...}', description: 'Source code files' },
22
- docs: { pattern: '**/*.{md,txt,...}', description: 'Documentation files' },
23
- config: { pattern: '**/*.{yaml,json,...}', description: 'Configuration files' },
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 (specify index or see all)
68
+ - "status" → Show index statistics
31
69
  - "list" → List all available indexes with stats
32
- - "reindex" → Re-index files (specify which index)
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: Initial indexing takes ~30s to load the embedding model.`,
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 will download the embedding model (~100MB) and set up the vector database.`
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 result = execSync("node .opencode/vectorizer/list-indexes.js 2>/dev/null || echo '{}'", {
73
- cwd: projectRoot,
74
- encoding: "utf8",
75
- timeout: 30000
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
- output += `### Usage\n`
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 += `codesearch({ query: "your query", index: "code" })\n`
116
- output += `codesearch({ query: "how to deploy", index: "docs" })\n`
117
- output += `\`\`\``
118
-
119
- return output
120
-
121
- } catch (error: any) {
122
- return `❌ Error listing indexes: ${error.message}`
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 = INDEX_PRESETS[indexName]?.description || "Custom index"
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
- \`\`\`bash
156
- npx opencode-workflow index --index ${indexName}
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
- \`\`\`bash
167
- npx opencode-workflow index --index ${indexName}
168
- # Or with specific directory:
169
- npx opencode-workflow index --index ${indexName} --dir src/
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 CLI
205
+ // REINDEX: Re-index using LOCAL vectorizer (no npm!)
175
206
  if (args.action === "reindex") {
176
207
  try {
177
- execSync(`npx opencode-workflow index --index ${indexName}`, {
178
- cwd: projectRoot,
179
- encoding: "utf8",
180
- timeout: 300000, // 5 min
181
- stdio: "pipe"
182
- })
183
-
184
- // Get stats after indexing
185
- const hashesFile = path.join(vectorsDir, indexName, "hashes.json")
186
- const hashes = JSON.parse(await fs.readFile(hashesFile, "utf8"))
187
- const fileCount = Object.keys(hashes).length
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
- **Files indexed:** ${fileCount}
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
- Try manually:
255
+ Make sure vectorizer is installed:
203
256
  \`\`\`bash
204
- npx opencode-workflow index --index ${indexName} --force
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 opencode-workflow vectorizer install
16
- * npx opencode-workflow index --index code
17
- * npx opencode-workflow index --index docs
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 opencode-workflow index --index <name>' first.`,
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 opencode-workflow vectorizer install`
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 opencode-workflow index --index code`
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 opencode-workflow index --index ${indexName}`
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 opencode-workflow index --index ${indexName} --force`
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 opencode-workflow index --index ${args.index || "code"} --force`
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
  })